Изменение отображения фрейма после каждого интервала, как анимация

#java #swing

#java #swing

Вопрос:

Как я могу использовать repaint здесь, чтобы он менял отображение через некоторый интервал, например, анимацию?

Вывод:

(Как изначально и случайным образом) this-1 (после некоторого короткого интервала и случайным образом он меняется на) this-2 и продолжается…

 import java.awt.*;
import java.util.Random;
import javax.swing.*;

class MatrixPanel extends JPanel {

    private final int sqW = 15;
    private final int sqH = 15; 

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

    @Override
    public void paintComponent(Graphics g) {
        Random rand = new Random();
        for (int i = 0; i < 300; i  = this.sqW) {
            for (int j = 0; j < 300; j  = this.sqH) {
                if (rand.nextInt(2) == 1) {
                    g.setColor(Color.BLACK);
                } else {
                    g.setColor(Color.GRAY);
                }
                g.fillRect(i, j, this.sqW, this.sqH);
                g.setColor(Color.BLACK);
                g.drawRect(i, j, this.sqW, this.sqH);
            }
        }
    }
}

class GameOfLifeGUI extends JFrame {

    public GameOfLifeGUI() {

        JSplitPane splitPane = new JSplitPane();

        /*Panels*/
        JPanel topPanel = new JPanel();
        JPanel bottomPanel = new JPanel();
        
        /*Label - 1*/
        JLabel generationLabel = new JLabel("GenerationLabel");
        generationLabel.setText("Generation #");
        generationLabel.setFont(new Font("Comic Sans MS", Font.PLAIN, 14));
        generationLabel.setBounds(10, 5, 300, 20);

        /*Label - 2*/
        JLabel aliveLabel = new JLabel("AliveLabel");
        aliveLabel.setText("Alive: ");
        aliveLabel.setFont(new Font("Comic Sans MS", Font.PLAIN, 14));
        aliveLabel.setBounds(10, 25, 300, 20);

        topPanel.setLayout(null);
        topPanel.add(generationLabel);
        topPanel.add(aliveLabel);

        bottomPanel.add(new MatrixPanel());

        setPreferredSize(new Dimension(315, 405));

        getContentPane().setLayout(new GridLayout());

        getContentPane().add(splitPane);

        splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
        splitPane.setDividerLocation(50);
        splitPane.setTopComponent(topPanel);
        splitPane.setBottomComponent(bottomPanel);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        pack();
    }

    public static void main(String[] args) {
        new GameOfLifeGUI().setVisible(true);
    }
}
  

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

Спасибо.

Комментарии:

1. (1 ) Хорошо, вы обновили код, чтобы переопределить getPreferredSize() метод. Это лучший подход для правильного использования Swing. Должен ли я использовать макет здесь? — общий ответ — да, всегда используйте менеджеры компоновки.. В этом случае большая часть вашего кода использует менеджер компоновки. По умолчанию a JPanel использует a FlowLayout , который учитывает размер MatrixPanel, теперь, когда вы внедрили getPreferredSize() метод. Вот почему ваш код работает так, как ожидалось.

2. Однако ваша «Верхняя панель» использует нулевой макет и setBounds(). Это не очень хорошая практика, и ее следует избегать. Итак, да, ваша верхняя панель должна использовать менеджер компоновки. На основе вашего текущего кода вы могли бы легко использовать BoxLayout . Прочитайте раздел из руководства Swing по менеджерам компоновки для получения дополнительной информации и рабочего примера. Если у вас возникли проблемы, отправьте новый вопрос с примером, который использует только вашу верхнюю панель , поскольку вы будете использовать layout manager только для отображения двух меток,

3. Также большинство людей, вероятно, не используют JSplitPane для этого. Вместо этого вы сохраняете BorderLayout JFrame по умолчанию. Затем вы добавляете «верхнюю панель» к BorderLayout.PAGE_START и MatrixPanel к BorderLayout.CENTER .

4. Большое спасибо @camickr , за все замечания. Я действительно постараюсь исправить все ошибки.

Ответ №1:

Вы не хотите делать следующее:

     public void paintComponent(Graphics g) {
        Random rand = new Random();
        for (int i = 0; i < 300; i  = this.sqW) {
            for (int j = 0; j < 300; j  = this.sqH) {
                if (rand.nextInt(2) == 1) {
                    g.setColor(Color.BLACK);
                } else {
                    g.setColor(Color.GRAY);
                }
                g.fillRect(i, j, this.sqW, this.sqH);
                g.setColor(Color.BLACK);
                g.drawRect(i, j, this.sqW, this.sqH);
            }
        }
    }
  

Поскольку рисование выполняется в EDT, вам необходимо свести обработку к минимуму, иначе производительность снизится, и вы можете даже заблокировать свое приложение.

Используйте таймер переключения и создайте изображение в объекте BufferedImage. Изображения с буферизацией предоставляют графический контекст, поэтому вы можете использовать те же методы, которые вы используете в paintComponent .

Вы можете установить интервал таймера на тот, который подходит вам, а затем выполнить перерисовку при создании изображения. Таким образом, это может выглядеть примерно так:

 final BufferedImage image = new BufferedImage(300,300,BufferedImage.TYPE_INT_RGB);
...     
Timer timer = new Timer(0, ae->createImage());
timer.setDelay(300);
timer.start();
...  
    
public void createImage(BufferedImage image) {
    Random rand = new Random();
    Graphics g = image.getGraphics();
    for (int i = 0; i < 300; i  = this.sqW) {
        for (int j = 0; j < 300; j  = this.sqH) {
            if (rand.nextInt(2) == 1) {
                g.setColor(Color.BLACK);
            } else {
                g.setColor(Color.GRAY);
            }
            g.fillRect(i, j, this.sqW, this.sqH);
            g.setColor(Color.BLACK);
            g.drawRect(i, j, this.sqW, this.sqH);
        }
    }
    repaint();
}
    
    
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    // Image observer not required so it can be set to null.
    g.drawImage(image, 0,0, null);
}
  

Наконец, вам нужно установить свой MatrixPanel размер, достаточно большой, чтобы вместить изображение. Вероятно 300 x 300 .

Комментарии:

1. Я попытался реализовать, как вы указали, но фрейм получил только одно поле посередине и мигает случайным образом, что, я думаю, связано с таймером, но фрейм не отображает все поля.

2. Вы делали что-то подобное? matrixPanel = new MatrixPanel(); matrixPanel.setPreferredSize(new Dimension(300,300)); bottomPanel.add(matrixPanel);

3. @Leprachon не используйте приведенное выше предложение для установки предпочтительного размера панели матрицы. Ваш текущий код, который переопределяет getPreferredSize() метод, является лучшим способом сделать это. Каждый компонент должен определять свой собственный предпочтительный размер. Обновите свой вопрос с помощью обновленного кода.

4. Да. Точно так же. Но вместо setPreferredSize() я использовал setSize() . Обязательно ли это делать так?

5. @Leprachon, нет. Не используйте setSize() или не используйте setPreferredSize(). Задача менеджера макета — установить размер / расположение компонентов на основе правил менеджера макета. Как я уже говорил в своем комментарии выше. Переопределение getPreferredSize() метода является правильным дизайном.