【问题标题】:Animations and SwingUtilities.invokeLater动画和 SwingUtilities.invokeLater
【发布时间】:2017-01-24 00:35:55
【问题描述】:

我想使用 SwingUtilities.invokeLater 方法,以便我的程序的所有 Swing 组件都由 事件调度线程 更新,因为这是一个好习惯。

但是,如果我将 main 方法的所有代码包装在 SwingUtilities.invokeLater(new Runnable { public void run() { /* code */ }}); 中,窗口就会冻结(这是正常的,因为我的代码有一个动画循环,需要几秒钟才能完成)。我应该把SwingUtilities.invokeLater 方法放在哪里?

程序代码(没有SwingUtilities.invokeLater 方法)

import java.awt.Color;
import java.awt.Dimension;
import java.awt.geom.Rectangle2D;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Test {

  public static void main(String[] args) {
    int width = 854;
    int height = 480;
    String title = "Test";
    BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    JFrame frame = new JFrame();
    JPanel panel = new JPanel() {
      protected void paintComponent(Graphics graphics) {
        super.paintComponent(graphics);
        Graphics2D graphics2D = (Graphics2D) graphics;
        graphics2D.drawImage(bufferedImage, 0, 0, null);
      }
    };
    frame.add(panel);
    frame.getContentPane().setPreferredSize(new Dimension(width, height));
    frame.pack();
    frame.setTitle(title);
    frame.setLocationRelativeTo(null);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
    int size = height/3;
    int x = -size;

    while (x <= width) {
      Graphics2D graphics2D = (Graphics2D) bufferedImage.getGraphics();
      graphics2D.setColor(Color.RED);
      graphics2D.fill(new Rectangle2D.Double(x, 0, size, size));
      graphics2D.setColor(Color.GREEN);
      graphics2D.fill(new Rectangle2D.Double(x, size, size, size));
      graphics2D.setColor(Color.BLUE);
      graphics2D.fill(new Rectangle2D.Double(x, 2 * size, size, size));
      graphics2D.dispose();
      panel.repaint();
      ++x;

      try {
        Thread.sleep(10);
      } catch (InterruptedException exception) {
        exception.printStackTrace();
      }
    }

    frame.dispose();
  }
}

程序截图

这是一个动画,其中三个彩色条逐渐延伸到窗口的右边缘。

【问题讨论】:

  • while-loop 正在阻止 EDT,阻止更新。 Swing Timer 可能是您正在寻找的解决方案。对于初学者,请查看How to use Swing Timers。您需要将计时器视为一个伪循环,每次计时,它都充当循环的迭代。因为它在 EDT 的上下文中更新,所以可以安全地用于更新 UI
  • 您的主要静态方法中有太多代码。实际上,您的程序不过是一个大的静态 main 方法。相反,您的代码应该努力更加符合 OOP,并且您的 main 方法应该只用于创建主类、将它们连接在一起、启动它们运行,仅此而已。
  • 动画,嗯……哦,你听说过 JavaFX 吗?
  • Swing 是为动画非常繁重的旧系统设计的。现在有了更强的 CPU 能力,加上硬件加速,Swing 有点过时了。 JavaFX 让动画的使用变得轻而易举。
  • 对于exampleexampleexample

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


【解决方案1】:

按照@MadProgrammer 的建议,这是更新为使用Timer 的自包含代码(顺便说一句,很好地发布该代码)。为了访问x 变量,它被移到为计时器定义的动作监听器中。为了从动作监听器访问Timer,它被移到了一个类属性中。后者意味着将大部分代码移动到对象实例的构造函数中更容易。

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.*;

public class Test {

    Timer timer;

    Test() {
        int width = 854;
        int height = 480;
        String title = "Test";
        BufferedImage bufferedImage = new BufferedImage(
                width, height, BufferedImage.TYPE_INT_RGB);
        JFrame frame = new JFrame();
        JPanel panel = new JPanel() {
            @Override
            protected void paintComponent(Graphics graphics) {
                super.paintComponent(graphics);
                Graphics2D graphics2D = (Graphics2D) graphics;
                // when you have an ImageObserver, may as well use it
                //graphics2D.drawImage(bufferedImage, 0, 0, null);
                graphics2D.drawImage(bufferedImage, 0, 0, this);
            }

            @Override
            public Dimension getPreferredSize() {
                return new Dimension(width,height);
            }
        };
        frame.add(panel);
        frame.pack();
        frame.setTitle(title);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        int size = height / 3;

        ActionListener animationListener = new ActionListener() {

            int x = -size;

            @Override
            public void actionPerformed(ActionEvent e) {
                if (x <= width) {
                    Graphics2D graphics2D = (Graphics2D) bufferedImage.getGraphics();
                    graphics2D.setColor(Color.RED);
                    graphics2D.fill(new Rectangle2D.Double(x, 0, size, size));
                    graphics2D.setColor(Color.GREEN);
                    graphics2D.fill(new Rectangle2D.Double(x, size, size, size));
                    graphics2D.setColor(Color.BLUE);
                    graphics2D.fill(new Rectangle2D.Double(x, 2 * size, size, size));
                    graphics2D.dispose();
                    panel.repaint();
                    ++x;
                } else {
                    timer.stop();
                    frame.dispose();
                }
            }
        };
        timer = new Timer(10, animationListener);
        timer.start();
    }

    public static void main(String[] args) {
        Runnable r = () -> {
            new Test();
        };
        SwingUtilities.invokeLater(r);
    }
}

【讨论】:

  • @MadProgrammer 有些人是杂种。我总是喜欢你的回答。鉴于这个很好的自给自足的例子,这是我的小“放纵”——我无法抗拒。如果人们想投反对票,他们可以破产。 ;)
  • 在回答这样的问题时,我也有一些被否决的答案,但正如你所做的那样,当我看到在自包含代码中的一些努力和一个很好的解释时,我也忍不住要回答 (1 + 顺便说一句)
  • 谢谢,这正是我想要的。现在我明白了Timer 类的目的。
  • 欢迎您。很高兴我能提供帮助,请继续发布可运行代码和明确问题的工作。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-25
  • 2011-11-04
  • 2011-01-29
  • 2011-07-02
  • 1970-01-01
相关资源
最近更新 更多