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

不问归期 提交于 2019-12-07 09:38:27

问题


I have a class (called Class_GUI) which has a panel with lots of buttons on it. Class_GUI has some methods that change the text and colour of the buttons.

I have also have a program with the actionPerformed method. When this is called it creates an instance of Class_GUI and repeatedly calls Class_GUI methods, changing the buttons etc.

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.

My attempt so far is in each Class_GUI method I do this at the end of the method:

SwingUtilities.invokeLater(Refresh_GUI);

Where Refresh_GUI is defined:

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

回答1:


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.

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.

The advantage though, is that the thread does not need to compete in order for the EDT to start processing the repaint request

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.




回答2:


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. 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.




回答3:


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).

Second, if you change any properties of a GUI component, it should automatically post an event to repaint itself. If not you can try invoking component.repaint(). But it's critical that the changes to the component properties happen on the EDT.




回答4:


A simple solution is execute the entire ActionPerformed event less task to clean the screen at the end of the event queue. So, first it executes the cleanScreen() function because the rest of the event waits for all events finish.

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


来源:https://stackoverflow.com/questions/13750659/in-java-how-do-i-repaint-a-panel-from-an-actionperformed-thread-while-it-is-curr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!