3

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

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秒后“考拉画”。有人可以告诉我这里发生了什么吗?问候。

4

2 回答 2

3
  1. 在这种情况下有效,因为您以编程方式冻结了 ouf Swing GUI,但是没有另一个更新,没有 另一个 JComponent

  2. 如果有一些其他更新到 Swing GUI,Thread.sleep(int) freeze Event Dispatch Thread,则不起作用,

  3. JComponents XxxModels默认情况下,对never的所有更新都将在JComponents view

  4. 例如,直到睡眠结束,您将丢失所有更新到 GUI

于 2013-03-24T15:24:39.093 回答
2

我打算在评论中提出的观点:

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

从某种意义上说,您无法预测会发生什么。我们能做的,就是猜测...

技术原因是大多数 ui 更新不会立即发生,而是按计划进行的:我们无法真正知道在我们身后等待的是什么。请记住:它只有一条车道,当在 actionPerformed 中时,是我们坐在其中。

出于教育原因,下面的代码是原始代码,需要取消/注释几行以演示不同的场景。

  • [0] 在睡眠之前/之后重置图标:正如您已经注意到的那样,即使获取了属性,第一个也不会显示。技术原因:视觉更新发生label.repaint()在 EDT 上安排的后续处理(如其 api 文档所述)
  • [1] 浏览 api 文档,我们注意到另一种方法:paintImmediately(...)它被记录为完全按照它的名字所说的做,并且允许 - 正如我们在 EDT 上一样 - 被允许被调用。看起来成功了,黄色图标出现了。
  • [2] 但请稍等:标签位于边界线的中心,无论如何都会填充该区域,与它是否具有图标无关。让我们试着把它放到一个需要重新布局的区域中,就像 fi 到南方一样。返回方块 [0],黄色图标未显示。
  • [3] 调查来源setIcon(..)显示布局是......再次安排。我们在方格 [1] 中了解到,如果布局是一对invalidate()/ ,我们可以强制事情立即发生validate()。宾果游戏,即使在南方也是黄色图标。
  • [4] 安排图标属性设置的讨厌的子类(注意:虽然这里是人为的,但它的契约中没有任何东西阻碍子类这样做!)。由于属性甚至没有设置,黄色没有显示,回到正方形 [0]

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

/**
 * 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();
            }
        });
    }
}
于 2013-03-25T12:16:08.297 回答