Search code examples
javaswinglook-and-feeljradiobutton

JRadioButton selection doesn't show on GUI until visible in Windows LaF


I'm currently working on a project that requires the state of a JRadioButton to change when the record being viewed is updated.

We've had a few clients complain to us that when the record changes, if the JRadioButton is off-screen, it won't be updated until the screen is shown. This behavior seems to be a result of using the Windows Look and Feel, as it doesn't seem to happen when it is not set.

The code example below demonstrates this. The default selected JRadioButton is 'Cat', so by selecting the 'Dog' JButton and then changing tab to 'Question', you can see the JRadioButton transition occur.

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

/**
 * Example of JRadioButton not updating until it's parent panel becomes visible.
 */
public class RadioButtonExample extends JPanel implements ActionListener {
    
    public static final String CAT  = "Cat";
    public static final String DOG  = "Dog";
    
    private final JRadioButton radCat;
    private final JRadioButton radDog;
    private final ButtonGroup grpAnimal;
    
    public RadioButtonExample() {
        super(new BorderLayout());
        
        JLabel lblQuestion = new JLabel("Are you a cat or dog person?");
        
        radCat = new JRadioButton(CAT);
        radCat.setActionCommand(CAT);
        radCat.setSelected(true);
        
        radDog = new JRadioButton(DOG);
        radDog.setActionCommand(DOG);
        
        grpAnimal = new ButtonGroup();
        grpAnimal.add(radCat);
        grpAnimal.add(radDog);
        
        JPanel pnlQuestion = new JPanel(new GridLayout(0, 1));
        pnlQuestion.add(lblQuestion);
        pnlQuestion.add(radCat);
        pnlQuestion.add(radDog);
        
        JButton btnSetCat = new JButton(CAT);
        btnSetCat.setActionCommand(CAT);
        btnSetCat.addActionListener(this);
        
        JButton btnSetDog = new JButton(DOG);
        btnSetDog.setActionCommand(DOG);
        btnSetDog.addActionListener(this);
        
        JPanel pnlButtons = new JPanel(new GridLayout(0, 1));
        pnlButtons.add(new JLabel("Update your choice of animal"));
        pnlButtons.add(btnSetCat);
        pnlButtons.add(btnSetDog);
        
        JTabbedPane tabPane = new JTabbedPane();
        tabPane.addTab("Buttons", pnlButtons);
        tabPane.addTab("Question", pnlQuestion);
        
        add(tabPane, BorderLayout.LINE_START);
        setBorder(BorderFactory.createEmptyBorder(20,20,20,20));
    }
    
    public void actionPerformed(ActionEvent evt) {
        SwingUtilities.invokeLater(new Runnable(){
            public void run() {
                grpAnimal.clearSelection();
                if (CAT.equals(evt.getActionCommand())) {
                    grpAnimal.setSelected(radCat.getModel(), true);
                }
                else if (DOG.equals(evt.getActionCommand())) {
                    grpAnimal.setSelected(radDog.getModel(), true);
                }
            }
        });
        
    }
    
    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private static void createAndShowGUI() {
        //Create and set up the window.
        JFrame frame = new JFrame("RadioButtonExample");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
        //Create and set up the content pane.
        JComponent newContentPane = new RadioButtonExample();
        newContentPane.setOpaque(true);
        frame.setContentPane(newContentPane);
 
        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }
    
    public static void main(String[] args) {
        // Comment out the line below to run using standard L&F
        setLookAndFeel();
        
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
    
    private static void setLookAndFeel() {
        try {
            // Set Windows L&F
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
        } 
        catch (Exception e) {
           // handle exception
        }
    }
}

Is there a way to prevent this behavior or even speed it up to make it less noticeable for our users?


Solution

  • You might be able to disable the animation by specifying the swing.disablevistaanimation Java system property:

    java -Dswing.disablevistaanimation="true" your-cool-application.jar
    

    In the com.sun.java.swing.plaf.windows.AnimationController class, there is a VISTA_ANIMATION_DISABLED field that is initialized to the swing.disablevistaanimation property. This field determines whether the paintSkin method calls the skin.paintSkinRaw method if animations are disabled or else starts to get into the animations.

    It works on my Windows 8.1 laptop with Java 8 (jdk1.8.0_65), so its effect does not seem to be limited to Windows Vista only.