4

我有一个类(称为 Class_GUI),它有一个面板,上面有很多按钮。Class_GUI 有一些方法可以改变按钮的文本和颜色。

我也有一个带有 actionPerformed 方法的程序。当它被调用时,它会创建一个 Class_GUI 实例并重复调用 Class_GUI 方法、更改按钮等。

我遇到的问题是,按钮仅在 actionPerformed 方法完全完成后才能正确显示,而我希望在调用每个 Class_GUI 方法后更改它。

到目前为止,我的尝试是在每个 Class_GUI 方法中,我在方法结束时执行此操作:

SwingUtilities.invokeLater(Refresh_GUI);

定义 Refresh_GUI 的地方:

Runnable Refresh_GUI = new Runnable(){
    public void run(){
        frame.revalidate();
        frame.repaint();
    }
};
4

4 回答 4

3

假设您的actionPerformed方法是在事件调度线程的上下文中调用的,在方法完成之前不会发生 UI 更新actionPerformed,即使 usingSwingUtilities#invokeLater也不会改变这一点,因为在actionPerformed方法退出之前,EDT 将无法继续处理(除其他外)重绘请求。

您能做的最好的事情是启动第二个线程并从该线程内更新您的 UI 组件……但是,您将被迫使用该区域,SwingUtilities#invokeLater因为您永远不应该更新 EDT 之外的任何 UI 组件。

不过,优点是线程不需要为了让 EDT 开始处理重绘请求而竞争

更新示例

public class SwingThreadUpdate {

    public static void main(String[] args) {
        new SwingThreadUpdate();
    }

    public SwingThreadUpdate() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new BlinkPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class BlinkPane extends JPanel {

        private JLabel label;
        private JButton button;

        public BlinkPane() {
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridy = 0;

            label = new JLabel("Blinky");
            label.setBackground(Color.RED);
            button = new JButton("Click me");

            add(label, gbc);
            gbc.gridy++;
            add(button, gbc);

            button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    button.setEnabled(false);
                    new Thread(new BlinkTask(BlinkPane.this)).start();
                }
            });

        }

        private void setBlink(boolean blink) {
            label.setOpaque(blink);
        }

        private void reset() {
            button.setEnabled(true);
            label.setOpaque(false);
        }
    }

    public class BlinkTask implements Runnable {

        private BlinkPane blinkPane;

        protected BlinkTask(BlinkPane blinkPane) {
            this.blinkPane = blinkPane;
        }

        @Override
        public void run() {
            Blink blinkOn = new Blink(blinkPane, true);
            Blink blinkOff = new Blink(blinkPane, false);

            for (int index = 0; index < 10; index++) {
                if (index % 2 == 0) {
                    SwingUtilities.invokeLater(blinkOn);
                } else {
                    SwingUtilities.invokeLater(blinkOff);
                }
                try {
                    Thread.sleep(125);
                } catch (InterruptedException ex) {
                }
            }

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    blinkPane.reset();
                }
            });

        }
    }

    public class Blink implements Runnable {

        private BlinkPane blinkPane;
        private boolean blink;

        public Blink(BlinkPane blinkPane, boolean blink) {
            this.blinkPane = blinkPane;
            this.blink = blink;
        }

        @Override
        public void run() {
            blinkPane.setBlink(blink);
            blinkPane.repaint();
        }
    }
}

您可能想阅读AWT 和 Swing 中的绘画以获取更多信息。

于 2012-12-06T19:16:18.437 回答
2

如果您的 actionPerform 方法调用代码以更新 for 循环中的按钮,您还可以在 invokeLater 中添加更新代码,这样更新和绘制代码将一一运行。Invoke later 将仅在当前方法完成执行后执行,因此确保绘制更快发生的唯一方法是将您的任务分解为更小的部分。

于 2012-12-06T19:07:30.743 回答
1

首先,确保您仅从 Event Dispatch 线程访问任何 GUI 组件(通过 invokeLater 或作为处理 GUI 事件的一部分)。

其次,如果您更改了 GUI 组件的任何属性,它应该会自动发布一个事件以重新绘制自己。如果没有,您可以尝试调用component.repaint(). 但至关重要的是,组件属性的更改发生在 EDT 上。

于 2012-12-06T19:07:05.843 回答
0

一个简单的解决方案是在事件队列的末尾执行整个 ActionPerformed 无事件任务来清理屏幕。因此,首先它执行 cleanScreen() 函数,因为事件的其余部分等待所有事件完成。

    AnyButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            cleanScreen();        //Modify components before action performer event
            EventQueue.invokeLater( new Runnable() {
                @Override public void run() {
                    anytask();    //Action performer event
                }
            });                     
        }
    });
于 2014-02-05T00:22:46.477 回答