java - 创建一个逐步更新 JPanel 的 ActionListener

标签 java swing timer

我正在尝试让 JButton 启动一个解决迷宫的动画。我有一个递归的深度优先搜索函数,可以找到路径,但逐渐为其着色(使用 Thread.sleep() )。该代码完美运行。我遇到的问题是让按钮启动此动画。

当我将函数添加到 JButton 的 actionPerformed 中时,解决方案只会在完全完成后才会出现(而不是一步一步)。我了解到这是因为 Action 监听器是单线程或类似的东西,所以我尝试使用 Swing Timer。我只是创建了一个每隔几毫秒重新绘制 JPanel 的事件,认为这会在后台继续重新绘制 JPanel,从而显示查找解决方案的进度。但这给了我一个运行时错误:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException at Game$1.actionPerformed(Game.java:54) at javax.swing.Timer.fireActionPerformed(Timer.java:313) at javax.swing.Timer$DoPostEvent.run(Timer.java:245) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756) at java.awt.EventQueue.access$500(EventQueue.java:97) at java.awt.EventQueue$3.run(EventQueue.java:709) at java.awt.EventQueue$3.run(EventQueue.java:703) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76) at java.awt.EventQueue.dispatchEvent(EventQueue.java:726) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93) at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

关于如何从 JButton 启动动画有什么想法吗?我觉得这应该比我制作的要简单得多,因为动画本身就可以正常工作,而我想要的只是通过单击 JButton 来启动它。我想我应该从零代码开始,并根据要求提供所需的一切。

Game.java

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

public class Game {
    private static MazeGrid scene;
    private static JFrame frame;
    private static JButton play;
    private static JButton solve;
    private static JButton exit;
    private static Timer timer;
    private static boolean s = false;


    public static void init() {
        scene = new MazeGrid();
        frame = new JFrame();
        play = new JButton("Play");
        solve = new JButton("Solve");
        exit = new JButton("Exit");
        frame.setResizable(false);
        frame.setSize(900, 700);
        frame.setLayout(null);
        frame.setLocationRelativeTo(null);

        scene.setSize(650, 680);
        frame.add(scene, BorderLayout.CENTER);
        play.setSize(100, 50);
        play.setLocation(650, 10);
        frame.add(play);
        solve.setSize(100, 50);
        solve.setLocation(770, 10);
        frame.add(solve);
        exit.setSize(100, 50);
        exit.setLocation(650, 100);
        frame.add(exit);
        frame.setVisible(true);
    }


    public static void main(String[] args) throws InterruptedException {
        ActionListener action = new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent ev) {
                if (ev.getSource() == timer) {
                    scene.repaint();
                    System.out.println("hello");
                }

            }
        };

        timer = new Timer(40, action);
        timer.setInitialDelay(0);
        timer.start();

        init();
        play.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                frame.setVisible(false);
                init();
            }
        });

        solve.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                scene.solve(1, 1, squares, 40);
                timer.stop();
            }
        });

        exit.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                frame.setVisible(false);
                frame.dispose();
            }
        });
    }

}

解决

private void solve(int x, int y, Squares squares, int delay) {
    // reached middle
    if (x == N / 2 && y == N / 2)
        done = true;
    if (x == 0 || y == 0 || x == N + 1 || y == N + 1)
        return;
    if (done || m.Visited(x, y))
        return;

    m.setVisited(x, y, true);

    if (!(x == 1 & y == 1)) {
        squares.getSquare(x, y).setPath(new Color(180, 140, 50));
        try {
            Thread.sleep(delay);
        } catch (InterruptedException e) {
            System.err.println("Error while sleeping!");
            Thread.currentThread().interrupt();
        }

        repaint();
    }

    if (!m.Top(x, y))
        solve(x, y + 1, squares, delay);
    if (!m.Right(x, y))
        solve(x + 1, y, squares, delay);
    if (!m.Bottom(x, y))
        solve(x, y - 1, squares, delay);
    if (!m.Left(x, y))
        solve(x - 1, y, squares, delay);

    if (done)
        return;
    if (!(x == 1 & y == 1)) {
        squares.getSquare(x, y).setPath(new Color(230, 230, 230));
        try {
            Thread.sleep(delay);
        } catch (InterruptedException e) {
            System.err.println("Error while sleeping!");
            Thread.currentThread().interrupt();
        }
        repaint();
    }
}

最佳答案

在启动 Timer 之前调用 init(),否则 scene 对象可能为 null尝试访问它。

     init();

     ActionListener action = new ActionListener(){
                @Override
                public void actionPerformed(ActionEvent ev) {
                    if (ev.getSource() == timer) {
                        scene.repaint();
                        System.out.println("hello");
                    }

                }
            };

     timer = new Timer(40, action);
     timer.setInitialDelay(0);
     timer.start();

关于java - 创建一个逐步更新 JPanel 的 ActionListener,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40782883/

相关文章:

java - JTable,TableColumnModelListener 检测选定的行

java - 调用服务时将二进制值(字节数组)设置为 SOAP 元素

java - Spring Boot使用@Value进行注入(inject)失败

java - 修改Java中的窗口菜单

java - 指定 GridBagLayout 中网格的数量

java - 以可变的时间间隔重复运行任务

linux - Linux内核add_timer的可靠性只有一个小问题吗?

java - 如何安排任务定期运行?

java - Java中的“大字典”实现

java - 使用 Gradle 安装 Mockito