In Java, how do I redraw a panel from an actionPerformed stream while it is running?

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

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

The problem I am facing is that the buttons are displayed only after the actionPerformed method has completed, whereas I want it to change after each Class_GUI method is called.

My attempt is still in every 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(); } }; 
+4
source share
4 answers

Assuming that your actionPerformed method actionPerformed called in the context of the Dispatching Event thread, no user interface updates will be executed until AFTER the actionPerformed method has actionPerformed , even with SwingUtilities#invokeLater , since the actionPerformed method completes, EDT will not be able to continue processing (among other things) redraw requests.

The best you can do is start the second thread and from within that thread, update the user interface components ... but you will be forced to use SwingUtilities#invokeLater , since you will NEVER update any user interface component outside of the EDT.

The advantage, however, is that the thread does not have to compete for EDT to start processing the redraw request.

UPDATED with an 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 may need to read "Painting in AWT and Swing" for more information.

+3
source

In the event that your actionPerform method calls the code to update the buttons in a for loop, you can also add the update code to invokeLater in such a way that both the update code and the drawing code will be executed one after another. A call will later be executed only after the current method completes its execution, so the only way to provide faster drawing is to break your tasks into smaller ones.

+2
source

First, make sure that you only get access to any GUI components from the Event Dispatch stream (via invokeLater or as part of handling a GUI event).

Secondly, if you change any properties of the GUI component, it must automatically publish the event in order to redraw it. If not, try calling component.repaint() . But it is important that component property changes occur on the EDT.

+1
source

A simple solution is to complete the entire ActionPerformed event less to clear the screen at the end of the event queue. So first, it executes the cleanScreen () function, because the rest of the event is waiting for all events to complete.

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

All Articles