Search code examples
javaswingjcomponent

How to repaint JComponent once after several repaint()?


My program loops on a list of JButton. For each JButton, it repaints it each second:

public class a {
    static JFrame frame = new JFrame();
    static Set<MyButton> components = new HashSet<MyButton>();

    public static void main(String[] args) {
        JPanel lPanel = new JPanel(new GridLayout(0,18));

        for (int i = 0; i < 500; i++) {
            MyButton lButton = new MyButton("Hello" + i);
            lPanel.add(lButton);
            components.add(lButton);
        }
        frame.add(lPanel);
        frame.pack();
        frame.setVisible(true);
        blinking();
    }

    public static void blinking() {
        Timer blinkTimer = new Timer(500, new ActionListener() {
            boolean on = false;

            public void actionPerformed(ActionEvent e) {
                for (MyButton lButton : components) {
                    lButton.blink(on);
                }
                on = !on;
            }
        });
        blinkTimer.start();
    }
}

public class MyButton extends JButton {

    public MyButton(String text) {
        super(text);
    }

    public void blink(boolean on) {
        setBackground(on ? Color.DARK_GRAY : Color.LIGHT_GRAY);
        setForeground(on ? Color.WHITE : Color.BLACK);
    }
}

Result: enter image description here

I want to ensure that all the buttons update their background and foreground color in the same time.

JButton extends JComponent, and JComponent.setBackground calls repaint(), as well as JComponent.setForeground.

Therefore, each second my program calls repaint() two times for each button, and the colors are not always updated at the same time.

How can I ensure that the colors of all the buttons are updated at the same time? I think the solution is to call only one repaint(), maybe on the buttons container, but how can I do that?


Solution

  • Solution: use setIgnoreRepaint() and repaint only the frame:

    frame.setIgnoreRepaint(true);
    for (JButton lButton : components) {
                        // blink the buttons background on and off
        lButton.blink(on);
    }
    frame.setIgnoreRepaint(false);
    frame.repaint();
    on = !on;
    

    The setIgnoreRepaint doc is a bit misleading since it says to suspend repaint for system events. So we are thinking of events that the OS could send. But in fact it ignores the repaints (implicit or explicit), and does not ignore the method calls directly on the component (paint / paintImmediatly ... all that).