简体   繁体   English

如何在多次 repaint() 后重绘 JComponent 一次?

[英]How to repaint JComponent once after several repaint()?

My program loops on a list of JButton.我的程序在 JButton 列表上循环。 For each JButton, it repaints it each second:对于每个 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);
    }
}

Result:结果: 在此处输入图片说明

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 . JButton extends JComponentJComponent.setBackground调用repaint() ,以及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.因此,我的程序每秒为每个按钮调用两次repaint() ,并且颜色并不总是同时更新。

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?我认为解决方案是只调用一个repaint() ,也许在按钮容器上,但我该怎么做?

Are the buttons all in the same layer of the panel?按钮是否都在面板的同一层? Then why not use the background of that layer instead of the buttons' background?那么为什么不使用该图层的背景而不是按钮的背景呢?

For example:例如:

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

I think this is the best you can do with a single core/thread.我认为这是单核/线程所能做的最好的事情。

Also, if you want other components between the buttons, where their background is not the same as the layer's one, then just make them opaque ( setOpaque(true) ), if they are not already.此外,如果您想要按钮之间的其他组件,它们的背景与图层的背景不同,那么只需使它们不透明( setOpaque(true) ),如果它们还没有。

Solution: use setIgnoreRepaint() and repaint only the frame:解决方案:使用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;

The setIgnoreRepaint doc is a bit misleading since it says to suspend repaint for system events. setIgnoreRepaint 文档有点误导,因为它说要暂停系统事件的重绘。 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).但实际上它忽略了重绘(隐式或显式),并且不会直接忽略组件上的方法调用(paint /paintImmediately ...所有这些)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM