Java-апплет — как добавить двойную буферизацию в JButton

В настоящее время я использую класс Applet для создания простой игры. Поскольку есть эффект мерцания, я добавил двойную буферизацию для Graphics компонентов, создав внеэкранный буфер следующим образом:

public class AppletTest extends Applet implements Runnable {

    Thread thread;
    Image img;
    Graphics gfx;

    public final int WIDTH = 700, HEIGHT = 500;

    public void init() {
        this.resize(WIDTH, HEIGHT);

        thread = new Thread(this);
        thread.start();

        img = createImage(WIDTH, HEIGHT); // off-screen buffering
        gfx = img.getGraphics();
    }
    public void draw(Graphics g) {
        gfx.setColor(Color.BLACK);
        gfx.fillRect(0, 0, WIDTH, HEIGHT);
        gfx.setColor(Color.WHITE);

        gfx.fillRect(50, 50, 100, 100);
        gfx.setFont(new Font("Century", Font.BOLD, 30));
        gfx.drawString("I feel good sometimes I don't", 200, 200);            

        g.drawImage(img, 0, 0, this); // draws the off-screen image
    }
    public void update(Graphics g) {
        draw(g);
    }

    public void run() {
        while(true) {
            repaint();
            try {
                Thread.sleep(5);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

Если вы запустите приложение, все компоненты/методы Graphics (.fillRect, .drawString и т. д.) отрисовываются в буфере за пределами экрана. Однако моя цель — добавить JButton в апплет, и, как и ожидалось, компонент JButton не загружается вне экрана (что означает мерцание).

Graphics gfx;
JButton button1;

public void draw(Graphics g) {
    setLayout(null);

    button1.setBounds(225, 400, 250, 50);
    button1.setFont(new Font("Courier", Font.PLAIN, 17));
    button1.setForeground(Color.WHITE);
    button1.setBackground(Color.DARK_GRAY);

    add(button1); // is it possible to draw the JButton on the off-screen buffer?
}

Как бы вы добавили загрузку за пределами экрана в компонент JButton?


person MattGotJava    schedule 17.05.2018    source источник
comment
Компоненты Swing уже имеют двойную буферизацию, проблема в том, что вы не соблюдаете цепочку рисования апплетов (не вызывая super.update и не передавая ей свой двойной буфер). Сказав это, апплеты мертвы — пора двигаться дальше. Лучшим решением было бы начать с JPanel, который уже имеет двойную буферизацию. Затем вы можете добавить это в любой контейнер, который вы хотите. Кроме того, не пытайтесь рисовать компоненты Swing, это намного больше, чем просто они были нарисованы.   -  person MadProgrammer    schedule 17.05.2018
comment
Согласно этому старому сообщению SO, в JPanel все еще может потребоваться двойная буферизация. Не уверен, что это полезно, но у ОП та же проблема, что и у вас, с мерцанием при использовании Jpanel. Ссылка: stackoverflow.com/questions/2063607/java-panel-double- буферизация   -  person Krishnanshu Gupta    schedule 17.05.2018
comment
@KrishnanshuGupta У ОП (связанного вопроса) возникают проблемы с мерцанием, потому что они нарушают систему рисования. При правильном использовании компоненты Swing по умолчанию имеют двойную буферизацию.   -  person MadProgrammer    schedule 17.05.2018


Ответы (1)


AppletJApplet) официально устарели, они больше не поддерживаются Java, Oracle, браузерами (или сообществом в целом)

Компоненты Swing по умолчанию имеют двойную буферизацию. Если вы правильно работаете с системой рисования, у вас не должно быть никаких мерцаний, если они есть, то это явный признак того, что вы делаете что-то не так.

Я бы порекомендовал взглянуть на Выполнение пользовательского рисования и Рисование в AWT и Swing, чтобы узнать больше о том, как работает система рисования Swing. .

Swing является однопоточным и не потокобезопасным. Это означает, что вы не должны выполнять какие-либо длительные операции в контексте потока диспетчеризации событий и не должны обновлять пользовательский интерфейс вне контекста EDT.

Дополнительные сведения см. в разделе Параллелизм в Swing.

Простое решение этих проблем — использовать Swing Timer, который можно использовать для планирования регулярных обновлений, выполняемых в контексте EDT.

Дополнительные сведения см. в разделе Как использовать таймеры Swing...

В качестве основного запускаемого примера...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static class TestPane extends JPanel {

        public static final int WIDTH = 700, HEIGHT = 500;

        public TestPane() {
            setLayout(new GridBagLayout());
            add(new JButton("Big fat button"));
            Timer timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    repaint();
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(WIDTH, HEIGHT);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(Color.BLACK);
            g2d.fillRect(0, 0, WIDTH, HEIGHT);
            g2d.setColor(Color.WHITE);

            g2d.fillRect(50, 50, 100, 100);
            g2d.setFont(new Font("Century", Font.BOLD, 30));
            g2d.drawString("I feel good sometimes I don't", 200, 200);

            g2d.dispose();
        }

    }

}

Хорошо, "Но я абсолютно, должен, без вопросов, использовать Applet ... ????, тогда мне вас жаль, но это не меняет того факта, что Swing уже имеет двойную буферизацию. Приведенный выше пример можно легко применить к J/Applet, просто создав экземпляр JPanel и добавив его в контейнер Applet

Swing использует алгоритм «пассивного рендеринга», если вам абсолютно необходим полный контроль, вы можете посмотреть BufferStrategy, который передает вам полный контроль над системой рисования, но вы не сможете использовать компоненты Swing, поскольку они обновляются подсистемой Swing.

person MadProgrammer    schedule 17.05.2018