Radio button doesn't change the state immediat

2019-08-05 04:36发布

问题:

In our project my teammate notice unusual behavior for radio button, when inside action listener there is SwingUtilites.invokeLater call. Archetecture of the action listener doesn't allow to avoid this call because is designed to start another thread, and then there is a switch back to AWT thread.

Is there a way to fix this? I mean change the state of displayed component.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.WindowConstants;

public class RadioButtonTest {

public static void main(String[] args) {
    try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
            | UnsupportedLookAndFeelException e1) {
        e1.printStackTrace();
    }
    JFrame frame = new JFrame();
    JPanel panel = new JPanel();
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.setSize(200, 200);
    frame.add(panel);
    ButtonGroup group = new ButtonGroup();
    JRadioButton b1 = new JRadioButton("Button 1");
    final JRadioButton b2 = new JRadioButton("Button 2");
    b2.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent arg0) {
            Runnable action = new Runnable() {

                @Override
                public void run() {
                    try {
                        Thread.sleep(2500);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        e.printStackTrace();
                    }
                }
            };
            SwingUtilities.invokeLater(action);
        }
    });
    group.add(b1);
    group.add(b2);
    panel.add(b1);
    panel.add(b2);

    frame.setVisible(true);
} 
}

回答1:

Use SwingWorker, try this code:

 public void actionPerformed(ActionEvent arg0) {
       SwingWorker<Object,Object> sw = new SwingWorker<Object,Object>()
       {
            @Override
            protected Object doInBackground() throws Exception
            {
                try {
                     Thread.sleep(2500);
                 } catch (InterruptedException e) {
                     Thread.currentThread().interrupt();
                     e.printStackTrace();
                 }
                 return null;
            }
        };
        sw.execute();
}

SwingWorker is executed on separate worker thread which is invoked by event dispatcher thread by calling execute method. SwingUtilities.invokeLater method just imposes run method to be executed asynchronously on the event dispatching thread, so calling Thread.sleep inside it will freezes the event dispatch thread which affects the GUI.



回答2:

It looks like you want to avoid repeatedly initiating a long running background task. Instead of a JRadioButton, use the parent JToggleButton, and set it's name and action to Cancel when the background task is started. A Future such as SwingWorker makes this convenient. A related example using JButton is seen here.