Java Swing: активируйте анимацию с помощью события Action

Я пытаюсь написать простой графический интерфейс в java.switch, который активирует анимацию при нажатии кнопки. Анимация работает, когда я удаляю кнопку и просто запускаю анимацию напрямую, но когда я помещаю код анимации в класс ActionListener, который запускается при нажатии кнопки, он больше не работает. В чем причина этого?

Код, рабочая анимация без кнопки:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.TimeUnit;

class SimpleGUI {
    
    private JFrame frame;
    private JPanel panel;
    private int xPos = 70;
    private int yPos = 70;

    class DrawPanel extends JPanel {
    public void paintComponent(Graphics g) {
        g.setColor(Color.white);
        g.fillRect(0, 0, this.getWidth(), this.getHeight());
        g.setColor(Color.green);
        g.fillOval(xPos, yPos, 40, 40);
    }
    }

    public void go() {
    frame = new JFrame();
    panel = new DrawPanel();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(BorderLayout.CENTER, panel);
    //frame.getContentPane().add(BorderLayout.SOUTH, button);
    frame.setSize(300,300);
    frame.setVisible(true);
    for(int i = 0; i < 100; i++) {
        xPos++;
        yPos++;
        panel.repaint();
        try {
        TimeUnit.MILLISECONDS.sleep(50);
        } catch(Exception e) {
        e.printStackTrace();
        }
    }
    }
}

class GUITest {
    public static void main(String[] args) {
    SimpleGUI gui = new SimpleGUI();
    gui.go();
    }
}

Код, неработающая анимация с кнопкой:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.TimeUnit;

class SimpleGUI {
    
    private JFrame frame;
    private JPanel panel;
    private int xPos = 70;
    private int yPos = 70;

    class DrawPanel extends JPanel {
    public void paintComponent(Graphics g) {
        g.setColor(Color.white);
        g.fillRect(0, 0, this.getWidth(), this.getHeight());
        g.setColor(Color.green);
        g.fillOval(xPos, yPos, 40, 40);
    }
    }

     class AnimationListener implements ActionListener {
    public void actionPerformed(ActionEvent event) {
        for(int i = 0; i < 100; i++) {
        xPos++;
        yPos++;
        panel.repaint();
            try {
            TimeUnit.MILLISECONDS.sleep(50);
        } catch(Exception e) {
            e.printStackTrace();
        }
        }
    }
     }

    public void go() {
    frame = new JFrame();
    panel = new DrawPanel();
    JButton button = new JButton("Start Animation");
    button.addActionListener(event -> button.setText("Animation starts"));
    button.addActionListener(new AnimationListener());
    
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(BorderLayout.CENTER, panel);
    frame.getContentPane().add(BorderLayout.SOUTH, button);
    frame.setSize(300,300);
    frame.setVisible(true);
    }
}



class GUITest {
    public static void main(String[] args) {
    SimpleGUI gui = new SimpleGUI();
    gui.go();
    }
}

Я ожидал, что код класса ActionListener будет выполнен полностью при нажатии кнопки. Я также попытался удалить цикл for в классе ActionListener, чтобы круг перемещался на один шаг при каждом нажатии кнопки. Это работает, и я могу переместить круг на все 100 шагов, несколько раз нажимая кнопку.

Таймер качания также работает в потоке диспетчеризации событий, однако, если он просто анимирует один шаг, а затем завершает работу, это возможно.

queeg 05.05.2024 13:30
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
2
53
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

У вас проблема с потоками.

Работающий: Вы запускаете приложение в основном потоке. Он создает пользовательский интерфейс, который вызывает Event Dispatching Thread для обновления пользовательского интерфейса. Параллельно ваше приложение запускает go() в основном потоке, и, похоже, оно работает. Хотя конструктивный недостаток все же есть.

Не работает: Метод go() запускается нажатием кнопки, которая сама вызывается в Event Dispatching Thread. Поскольку метод go() не завершается, поток остается занятым и у него нет времени на обновление пользовательского интерфейса.

Решение:

  • Используйте несколько потоков.
  • Чтобы избежать особенностей, научитесь обрабатывать параллелизм в Swing.

Есть хороший учебник: https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html

Вам нужен Timer. например

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

class SimpleGUI {

    private JButton bnStart;
    private JFrame frame;
    private DrawPanel panel;
    private int xPos = 70;
    private int yPos = 70;
    private Timer timer;

    class DrawPanel extends JPanel implements ActionListener {
        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.white);
            g.fillRect(0, 0, this.getWidth(), this.getHeight());
            g.setColor(Color.green);
            g.fillOval(xPos, yPos, 40, 40);
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            xPos++;
            yPos++;
            panel.repaint();
            if (xPos > 170) {
                timer.stop();
                bnStart.setText("Start Animation");
            }
        }
    }

    public void go() {
        frame = new JFrame();
        panel = new DrawPanel();
        bnStart = new JButton("Start Animation");
        bnStart.addActionListener(event -> {
            xPos = yPos = 70;
            bnStart.setText("Animation starts");
            timer = new Timer(50, panel);
            timer.start();
        });
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(BorderLayout.CENTER, panel);
        frame.getContentPane().add(BorderLayout.SOUTH, bnStart);
        frame.setSize(300, 300);
        frame.setVisible(true);
    }
}

public class GUITest {
    public static void main(String[] args) {
        SimpleGUI gui = new SimpleGUI();
        gui.go();
    }
}

Другие вопросы по теме