在Java中,如何在当前运行的actionPerformed线程中重新绘制面板?

时间:2012-12-06 19:02:59

标签: java swing concurrency actionlistener event-dispatching

我有一个类(称为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 个答案:

答案 0 :(得分:3)

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

您可以做的最好的事情是,启动第二个线程,并从该线程中更新您的UI组件......但是,您将被迫使用SwingUtilities#invokeLater区域,因为您永远不应更新任何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();
        }
    }
}

您可能希望阅读Painting in AWT and Swing以获取更多信息。

答案 1 :(得分:2)

如果你的actionPerform方法调用代码来更新for循环中的按钮,你也可以在invokeLater中添加更新代码,这样更新和绘制代码将逐个运行。稍后调用将仅在当前方法完成其执行后执行,因此只有确保绘制更快的方法才能将您的任务分解为更小的peices。

答案 2 :(得分:1)

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

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

答案 3 :(得分: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
                }
            });                     
        }
    });