更改 JButtons 背景的最佳方法

Posted

技术标签:

【中文标题】更改 JButtons 背景的最佳方法【英文标题】:Best way of Changing the background of JButtons 【发布时间】:2011-09-01 08:28:59 【问题描述】:

现在我使用

更改按钮的背景颜色
button.setBackground(Color.WHITE);

这是一个例子。

但是当我有一个巨大的 jbuttons 网格(1000+)时,只需运行一个 for 循环来更改每个按钮的背景非常非常慢。您可以看到网格逐渐变白,逐个框。我真的不想要这个

有没有更好的方法将网格上的每个 JButton 同时更改为相同的颜色?

这就是我制作网格的方式,使用的数字仅用于示例...

grid = new JPanel(new GridLayout(64, 64, 0, 0));

这是 4096 个按钮,大约需要 30 多秒才能将每个按钮更改为相同的颜色。

编辑 1:我需要按钮是可点击的,例如当我点击一个按钮时它会变成蓝色。单击所有按钮后,将每个按钮的颜色更改为白色。现在我可以正常工作,但是更改每个按钮的颜色很慢。

编辑 2:这就是我更改按钮的方式:

    new javax.swing.Timer(300, new ActionListener() 
        int counter = 0;
        public void actionPerformed(ActionEvent e) 
            if (counter >= counterMax) 
                ((Timer) e.getSource()).stop();
            
            Color bckgrndColor = (counter % 2 == 0) ? flashColor : Color.white;
            for (JButton button : gridButton) 
                button.setBackground(bckgrndColor);
            
            counter++;
        
    ).start();

【问题讨论】:

考虑在这里使用 JTable 而不是充满按钮的 GridLayout。 另外,您在哪个线程中进行背景设置?尝试在事件调度线程中执行此操作(使用 (SwingUtilities|EventQueue).(invokeLater|invokeAndWait)(...))。 另见flyweight pattern。 【参考方案1】:

您看到框被单独重新绘制这一事实表明双缓冲已关闭,或者按钮 UI 中的绘制代码使用了paintImmediately()

我使用 64x64 JButton 测试了您的设置,确保所有 UI 操作都在 EDT(事件调度线程)中执行。我可以确认您看到的效果,更改所有按钮的背景大约需要 1200 毫秒,每个框都会立即重新绘制。 您可以通过将网格设置为之前不可见和更改背景后可见来绕过立即重绘:

grid.setVisible(false);
for (Component comp : grid.getComponents()) 
   comp.setBackground(color);

grid.setVisible(true);

这导致网格只进行一次重绘,并将时间减少到约 300 毫秒(因子 4)。

这对于频繁更新来说仍然太慢了,所以你最好使用一个绘制网格的自定义组件,或者一个轻量级容器(在你的问题的评论中建议什么垃圾神)如果您希望允许网格单元是任意组件。

【讨论】:

【参考方案2】:

如果只需要重新绘制 可见 按钮,您可以获得相当大的好处。在下面显示的MVC 方法中,每个按钮都会监听一个定义其当前状态的模型。与重新绘制相比,更新模型非常快。虽然启动需要几秒钟,但我看到更新需要 JTable 使用的flyweight pattern 那样可扩展,如图here,但它可以发挥作用。

import java.awt.*;
import java.awt.event.*;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
import javax.swing.*;

/** @see https://***.com/questions/6117908 */
public class UpdateTest 

    private static final int ROW = 64;
    private static final int COL = 64;
    private static final int MAX = COL * ROW;
    private final DataModel model = new DataModel(MAX);

    public static void main(String[] args) 
        EventQueue.invokeLater(new Runnable() 

            @Override
            public void run() 
                new UpdateTest().create();
            
        );
    

    void create() 
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(ROW, COL));
        for (int i = 0; i < MAX; i++) 
            panel.add(new ViewPanel(model, i));
        
        Timer timer = new Timer(1000, new ActionListener() 

            @Override
            public void actionPerformed(ActionEvent e) 
                long start = System.nanoTime();
                model.update();
                System.out.println(
                    (System.nanoTime() - start) / (1000 * 1000));
            
        );
        JFrame f = new JFrame("JTextTest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new JScrollPane(panel), BorderLayout.CENTER);
        f.setPreferredSize(new Dimension(800, 600));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
        timer.start();
    

    private static class ViewPanel extends JPanel implements Observer 

        private final JButton item = new JButton();
        private DataModel model;
        private int index;

        public ViewPanel(DataModel model, int i) 
            this.model = model;
            this.index = i;
            this.add(item);
            item.setText(String.valueOf(i));
            item.setOpaque(true);
            item.setBackground(new Color(model.get(index)));
            model.addObserver(this);
        

        @Override
        public void update(Observable o, Object arg) 
            int value = model.get(index);
            item.setBackground(new Color(value));
        
    

    private static class DataModel extends Observable 

        private final Random rnd = new Random();
        private final int[] data;

        public DataModel(int n) 
            data = new int[n];
            fillData();
        

        public void update() 
            fillData();
            this.setChanged();
            this.notifyObservers();
        

        public int get(int i) 
            return data[i];
        

        private void fillData() 
            for (int i = 0; i < data.length; i++) 
                data[i] = rnd.nextInt();
            
        
    

【讨论】:

hmmm 非常好,+1 但我有两个问题 1) 为什么是 'item.setOpaque(true);'对于 Swing JComponents 2) 哪些方法在 JButtons 之间创建了间隙,并且不可能删除 10 像素(我尝试了 set fe ...(new GridLayout(ROW, COL, 0, 0)); 与 item.setPreferredSize(new Dimension( int, int));,嗯,真奇怪…… @mKorbel:好问题。 1) 在com.apple.laf.AquaLookAndFeel 中,Button.opaque 属性为false。 2) BasicButtonUI 使用 Button.margin 属性。另见UIManager Defaults。 我无法修复它,因为其他东西造成了差距,但很好的笑话,看起来就像在 JButton f.e. 中一样。尝试放在这里 :-) 与 item.setMargin(new Insets(50, 50, 50, 50));或 UIManager.put("Button.margin", new Insets(50, 50, 50, 50));,忘了我离开了... :-)

以上是关于更改 JButtons 背景的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

在运行时更改AngularJs中背景图像的最佳方法

如何向我的 JButtons 添加图像功能

如何样式按钮悬停并单击前景?

如何通过按 Fl_Button 更改 Fl_Window 的背景颜色

Zurb Foundation 4:如何在悬停时更改顶栏部分的背景颜色?

使按钮的背景颜色在单击后更改并保持不变