invokeLater没有按预期工作(JButton从未发布)

时间:2015-01-08 12:06:15

标签: java multithreading swing event-dispatch-thread invokelater

我有:

  • 带有JButton的JFrame。
  • 用于显示动画的单独Canvas子类。

我希望,在JButton的新闻界推出一个新的JFrame,显示Canvas子类,因为它动画。

我现在面临的问题是新的JFrame出现了,但它没有机会渲染任何东西,主框架上的JButton仍然处于低位状态。我认为这背后的逻辑是,EDT还没有完成它的工作,例如将JButton显示为已释放,因此没有机会运行动画方法并最终陷入僵局。

这个逻辑过去对我很好,因为我通过创建一个新线程来完成这项工作,但是最近我学到了更多关于Java,线程和Swing的知识,我已经知道所有Swing相关的事件必须在一个线程上处理:美国东部时间。

这让我感到困惑,因为我之前如何使它工作,但让我相信使用invokeLater可以帮助解决问题;因为让JFrame可见并且显示动画的工作将被放置在队列的末尾,允许JButton取消等等。但是我没有运气;我完全误解了什么吗?

谢谢!

(也请不要评论我使用Canvas类而不是JPanel,我有我的理由。)

示例代码:

Test5 (使用main方法的类)。

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

public class Test5 {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                  new Test5().setup();
              }
            });
          }
    private void setup() {
        JFrame frame = new JFrame("Test");
        JButton button = new JButton("Click here");
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        newFrame();
                    }
                  });
            }
        });
        frame.getContentPane().add(button);
        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }
    private void newFrame() {
        JFrame newFrame = new JFrame("The new frame");
        newFrame.setVisible(true);
        newFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        CanvasClass canvas = new CanvasClass();
        newFrame.getContentPane().add(canvas);
        newFrame.pack();
        canvas.runAnimation();
    }
}

CanvasClass (Canvas子类)

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;


public class CanvasClass extends Canvas {
    int x;
    public CanvasClass() {
        setSize(new Dimension(550,550));
        this.x = (int) (Math.random() * 255);
    }

    //@Override
    public void paint(Graphics g) {
        g.setColor(new Color(x, x, x));
        g.fillOval(0,0,500,500);
    }

    void runAnimation() {
        while (true) {
            randomise();
            repaint();
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    void randomise() {
    x = (int) (Math.random() * 255);
    }
}

1 个答案:

答案 0 :(得分:3)

你实际上是在EDT中调用它,但它在canvas.runAnimation();

中被阻止了

将要执行的代码放在单独的Thread(您可以在其中调用睡眠状态),但在repaint()

中调用SwingUtilities.invokeLater()

甚至更好地定义javax.swing.Timer并在定时器的runAnimation()

中调用actionPerformed()

更新:

  int delay = 20; //milliseconds
  ActionListener taskPerformer = new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
          canvasInstance.randomise();
          canvasInstance.repaint();
      }
  };
  new Timer(delay, taskPerformer).start();
要调用

而不是runAnimation()