Как перекрасить JComponent один раз после нескольких repaint ()? - PullRequest
2 голосов
/ 17 января 2020

Моя программа зацикливается на списке JButton. Для каждого JButton он перерисовывает его каждую секунду:

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

Результат: enter image description here

Я хочу убедиться, что все кнопки обновляют свой цвет фона и переднего плана в то же время.

JButton extends JComponent и JComponent.setBackground вызывает repaint(), а также JComponent.setForeground.

Поэтому каждую секунду моя программа вызывает repaint() два раза для каждая кнопка и цвета не всегда обновляются одновременно.

Как я могу гарантировать, что цвета всех кнопок обновляются одновременно? Я думаю, что решение состоит в том, чтобы вызвать только один repaint(), может быть, на контейнере кнопок, но как я могу это сделать?

Ответы [ 2 ]

1 голос
/ 17 января 2020

Решение: используйте setIgnoreRepaint() и перерисовывайте только кадр:

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

setIgnoreRepaint do c немного вводит в заблуждение, так как говорит о приостановке перерисовки для системных событий. Итак, мы думаем о событиях, которые ОС может отправить. Но на самом деле он игнорирует перерисовки (неявные или явные) и не игнорирует вызовы методов непосредственно для компонента (paint / paintImmediatly ... все это).

1 голос
/ 17 января 2020

Все ли кнопки находятся в одном слое панели? Тогда почему бы не использовать фон этого слоя вместо фона кнопок?

Например:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Main {
    private static JButton newButton() {
        final JButton b = new JButton("x");
        b.setContentAreaFilled(false);
        //b.setOpaque(false); //Do not make this call, because setContentAreaFilled documentation says so.
        return b;
    }

    public static void main(final String[] args) {
        final JButton tester = newButton();
        final Dimension testerDim = tester.getPreferredSize();
        final Dimension maxDim = new Dimension(1200, 700);

        final int rows = maxDim.width / testerDim.width,
                  cols = maxDim.height / testerDim.height;

        final JPanel buttonPanel = new JPanel(new GridLayout(0, cols));
        final int n = rows * cols;
        buttonPanel.add(tester);
        for (int i = 1; i < n; ++i)
            buttonPanel.add(newButton());

        final JFrame frame = new JFrame("Button grid");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(buttonPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        final Color bg = buttonPanel.getBackground();
        final Timer timer = new Timer(500, new ActionListener() {
            private boolean on = false;

            @Override
            public void actionPerformed(final ActionEvent e) {
                buttonPanel.setBackground(on? bg: bg.darker());
                on = !on;
            }
        });
        timer.setRepeats(true);
        timer.start();
    }
}

Я думаю, что это лучшее, что вы можете сделать с одним ядром / потоком.

Кроме того, если вы хотите, чтобы между кнопками находились другие компоненты, фон которых не совпадает с фоном слоя, просто сделайте их непрозрачными (setOpaque(true)), если их еще нет.

...