简体   繁体   English

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

[英]In Java how do I repaint a panel from an actionPerformed thread while it is currently running?

I have a class (called Class_GUI) which has a panel with lots of buttons on it. 我有一个类(称为Class_GUI),它的面板上有很多按钮。 Class_GUI has some methods that change the text and colour of the buttons. Class_GUI有一些方法可以更改按钮的文本和颜色。

I have also have a program with the actionPerformed method. 我也有一个带有actionPerformed方法的程序。 When this is called it creates an instance of Class_GUI and repeatedly calls Class_GUI methods, changing the buttons etc. 调用此方法时,它将创建Class_GUI的实例,并反复调用Class_GUI方法,更改按钮等。

The issue I'm having is that the buttons only display properly once the actionPerformed method has finished entirely whereas I want it to change after each Class_GUI method is called. 我遇到的问题是,仅在actionPerformed方法完全完成后,按钮才能正确显示,而我希望在调用每个Class_GUI方法后对其进行更改。

My attempt so far is in each Class_GUI method I do this at the end of the method: 到目前为止,我的尝试是在每个Class_GUI方法中,我在方法末尾执行此操作:

SwingUtilities.invokeLater(Refresh_GUI);

Where Refresh_GUI is defined: 定义Refresh_GUI的位置:

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

Assuming that your actionPerformed method is being called within the context of the Event Dispatching Thread, no UI updates will occur until AFTER the actionPerformed method has competed, even using SwingUtilities#invokeLater won't change that, because until the actionPerformed method exits, the EDT won't be able to continue processing (amongst other things) repaint requests. 假设你actionPerformed方法被调用的事件指派线程的上下文中,将不会出现UI更新后才actionPerformed方法竞争,甚至使用SwingUtilities#invokeLater不会改变,因为直到actionPerformed方法退出,EDT (除其他事项外)将无法继续处理重画请求。

The best you can do, is start a second thread and from within that thread, update your UI components...but, you area going to be forced to use SwingUtilities#invokeLater as you should NEVER update any UI component outside the EDT. 最好的办法是启动第二个线程,并从该线程中更新UI组件...但是,您将不得不使用SwingUtilities#invokeLater因为您切勿更新EDT之外的任何UI组件。

The advantage though, is that the thread does not need to compete in order for the EDT to start processing the repaint request 但是,优点是,线程不需要竞争即可让EDT开始处理重绘请求。

UPDATED with Example 用示例更新

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();
        }
    }
}

You might like to have a read through Painting in AWT and Swing for more information. 您可能希望通读AWT和Swing中的绘画以获取更多信息。

Incase your actionPerform method calls code to update buttons in a for loop you could also add the updation code in the invokeLater that way both the updation and painting code will run one by one. 如果您的actionPerform方法调用代码以更新for循环中的按钮,则还可以在invokeLater中添加更新代码,这样,更新代码和绘画代码将一一运行。 Invoke later will execute only after current method completes its execution so only way to ensure painting happens faster is to break your tasks into smaller peices. 稍后调用仅在当前方法完成执行后才执行,因此确保绘画更快进行的唯一方法是将您的任务分解为较小的部分。

First, make sure you are only accessing any GUI components from the Event Dispatch thread (via invokeLater or as part of handling a GUI event). 首先,确保仅从事件分发线程中访问任何GUI组件(通过invokeLater或作为处理GUI事件的一部分)。

Second, if you change any properties of a GUI component, it should automatically post an event to repaint itself. 其次,如果您更改了GUI组件的任何属性,它应该自动发布一个事件以重新绘制自身。 If not you can try invoking component.repaint() . 如果没有,您可以尝试调用component.repaint() But it's critical that the changes to the component properties happen on the EDT. 但是至关重要的是,对组件属性的更改必须在EDT上进行。

A simple solution is execute the entire ActionPerformed event less task to clean the screen at the end of the event queue. 一个简单的解决方案是执行整个ActionPerformed事件较少的任务,以清理事件队列末尾的屏幕。 So, first it executes the cleanScreen() function because the rest of the event waits for all events finish. 因此,首先它执行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
                }
            });                     
        }
    });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM