线程睡眠在actionPerformed方法中

时间:2013-03-24 15:18:46

标签: java swing event-dispatch-thread thread-sleep

首先,我想说我知道这种方法是错误的所以我问这个问题是因为纯粹的好奇心。假设我有一个这样的swing应用程序:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ThreadSleeping {
    JFrame frame = new JFrame();
    JPanel panel = new JPanel();
    JButton button = new JButton("Load");
    JLabel label = new JLabel();

    public ThreadSleeping() {
        panel.add(button);

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                label.setIcon(new ImageIcon(
                        "C:/Users/Public/Pictures/Sample Pictures/Tulips.jpg"));
                System.out.println("Tulips painted");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                label.setIcon(new ImageIcon(
                        "C:/Users/Public/Pictures/Sample Pictures/Koala.jpg"));
                System.out.println("Koala painted");

            }
        });

        frame.add(panel, BorderLayout.NORTH);
        frame.add(label, BorderLayout.CENTER);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setSize(1024, 768);
        // frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ThreadSleeping();
            }
        });
    }
}

基本上,当我单击Load按钮时,我希望显示Tulips.jpg图像,然后GUI会冻结2秒钟,之后我希望显示Koala.jpg图像。但是会发生什么:我点击按钮,GUI冻结2秒钟,Koala.jpg显示。之前没有Tulips.jpg。但让我感到困惑的是当我放置System.out.println("Tulips painted");System.out.println("Koala painted");时。因此,当我点击按钮时,它会打印“郁金香画”,并在2秒后“考拉画”。谁能告诉我这里发生了什么?问候。

2 个答案:

答案 0 :(得分:3)

  1. 适用于这种情况,because you programatically freeze ouf Swing GUI, but there is/aren't another update(s),或其他JComponent

  2. 如果有一些其他更新到Swing GUI,Thread.sleep(int)冻结事件调度线程

  3. ,则不起作用
  4. 默认情况下,JComponents XxxModels

  5. 上永远不会显示JComponents view的所有更新内容
  6. example until sleep ended you'll lost all updated to the GUI

答案 1 :(得分:2)

我打算在评论中提出这一点:

  

如果你睡觉了edt,那么产生的错误行为基本上是不可预测的。

不可预测的,因为您无法知道会发生什么。我们所能做的就是猜...

技术原因是大多数ui更新不是立即发生的,而是安排的:我们无法真正知道在我们后面排队等待的是什么。请记住:它只是一条通道,当它在actionPerformed中时,它就是我们坐在它里面。

出于教育原因,下面的代码是原始代码,有几行代替/评论以演示不同的场景。

  • [0]在睡眠之前/之后重置图标:正如您已经注意到的那样,即使采用属性,第一个也不显示。技术原因:视觉更新通过label.repaint()进行,该paintImmediately(...)计划在EDT进行后续处理(如其api doc所述)
  • [1]浏览api文档,我们注意到另一种方法:setIcon(..)被记录为完全按照名称所说和允许 - 正如我们在EDT上 - 被允许被调用。看起来很成功,黄色图标显示出来。
  • [2]但是等待:在边界线的中心,标签无论如何都会填充该区域,而不管它是否有图标。让我们尝试将它放入需要重新布局的区域,如f.i.进入南方。回到正方形[0],黄色图标不显示。
  • [3]查看invalidate()的来源显示布局已......再次安排。我们在square [1]中了解到,如果是validate() / /** * Unpredictable behaviour when sleeping the EDT. * http://stackoverflow.com/q/15600203/203657 * * [0] run as-is: property set but yellow not showing * [1] uncomment paintImmediately: yellow showing in center * [2] add label to south: yellow not showing * [3] force immediate in-/validation: yellow showing in south * [4] subclass label with invoked property setting: * property not set, yellow not showing * */ public class ThreadSleeping { JFrame frame = new JFrame(); JPanel panel = new JPanel(); JButton button = new JButton("Load"); JLabel label = new JLabel() { // [4] subclass implemented to schedule the property setting // @Override // public void setIcon(final Icon icon) { // SwingUtilities.invokeLater(new Runnable() { // public void run() { // superSetIcon(icon); // // } // }); // } // // protected void superSetIcon(Icon icon) { // super.setIcon(icon); // } // }; public ThreadSleeping() { panel.add(button); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { Icon firstIcon = new FixedIcon(Color.YELLOW, 100, 100); Icon secondIcon = new FixedIcon(Color.RED, 500, 500); label.setIcon(firstIcon); // check if the property is already set System.out.println(label.getIcon()); // following lines try to force the output before going to sleep // [3] paintImmediately + force immediate re-layout // label.invalidate(); // label.getParent().validate(); // {1] paintImmediately (fine for center, no effect for south) // ((JComponent) label.getParent()).paintImmediately(0, 0, 5000, 5000); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } label.setIcon(secondIcon); } }); frame.add(panel, BorderLayout.NORTH); // un/comment one of the following, placing the // label either in CENTER (= sized to fill) frame.add(label, BorderLayout.CENTER); // [2] or in SOUTH (= sized to pref) // frame.add(label, BorderLayout.SOUTH); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setSize(1024, 768); frame.setVisible(true); } /** * Simple fixed size Icon filling its area. */ public static class FixedIcon implements Icon { private Color background; private int width; private int height; public FixedIcon(Color background, int width, int height) { this.background = background; this.width = width; this.height = height; } @Override public void paintIcon(Component c, Graphics g, int x, int y) { g.setColor(background); g.fillRect(0, 0, width, height); } @Override public int getIconWidth() { return width; } @Override public int getIconHeight() { return height; } @Override public String toString() { return "[" + background + " w/h " + width + "/" + height + "]"; } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new ThreadSleeping(); } }); } } 对的布局,我们可以立即强制发生。宾果,黄色图标,即使在南方。
  • [4]令人讨厌的子类,它安排了图标属性设置(注意:虽然在这里设计了它的合同中没有任何东西妨碍子类做到这一点!)。由于属性未设置,黄色不显示,返回方[0]

在一天结束时(但在睡觉之前,EDT :-),在睡眠期间无法可靠地预测视觉结果。视觉效果只是冰的一角......

{{1}}