使用FormLayout批量重绘Swing组件

时间:2014-12-04 20:48:18

标签: java swing jbutton form-layout

我正在制作触摸屏键盘。当我按shift(从小写到大写)时,我希望字母键改变文本。这是我当前实现的一小部分:

public void updatedButtons()
{
    switch( m_state )
    {
        case QWERTY:
            for( KeyboardButton button : m_keyboardButtons )
            {
                button.setText( button.getQwertyText().toLowerCase() );
            }
            break;
        case QWERTY_SHIFT:
            for( KeyboardButton button : m_keyboardButtons )
            {
                button.setText( button.getQwertyText().toUpperCase() );
            }
            break;
    }
}

其中KeyboardButton是JButton的简单扩展,带有qwertyText字符串字段。

这里的问题是按钮以混乱的方式更新。我明白为什么会这样;当我调用setText()时,它会为单个组件调用repaint(),并且每个按钮都会发生这种情况。我的问题是,我是否能够“批量重绘”这些按钮,而不会破坏Swing的设计(我不想覆盖AbstractButton的setText()方法)。感谢。

更新

原来这是FormLayout的一个问题。下面是说明问题的简单代码(请注意,您将需要一个JGoodies Form Jar,并且可能需要修改断点值。)

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

public class JButtonTest
{
    public static List<JButton> buttons = new ArrayList<JButton>();
    public static boolean isCaps = false;
    public static JFrame frame;


    public static void main(String[] args)
    {
        frame = new JFrame();
        frame.setSize( 1000, 100 );

        JPanel panel = new JPanel();
        // Form with 11 83x83 pixel squares, with 5x83 pixel spaces.
        panel.setLayout( new FormLayout("83px, 5px, 83px, 5px, 83px, 5px, 83px, 5px, "
                + "83px, 5px, 83px, 5px, 83px, 5px, 83px, 5px, 83px, 5px, 83px, 5px, 83px, 5px, 86px",
                                        "83px"));

        int i = 1;
        for (char c = 'a'; c < 'l'; ++c)
        {
            JButton button = new ComplexButton( Character.toString(c) );

            button.addActionListener( new ActionListener()
            {
                @Override
                public void actionPerformed( ActionEvent e )
                {
                    updateButtons();
                }
            });

            panel.add( button, new CellConstraints().xywh( (i*2)-1, 1, 1, 1, CellConstraints.FILL, CellConstraints.FILL) );
            buttons.add( button );
            ++i;
        }

        frame.setContentPane( panel );
        frame.setVisible( true );
    }

    // Enable the commented-out lines in this method to allow concurrent updating.
    public static void updateButtons()
    {
        for (JButton button : buttons)
        {
            if (!isCaps)
                button.setText( button.getText().toUpperCase() );
            else
                button.setText( button.getText().toLowerCase() );
            //RepaintManager.currentManager( button ).markCompletelyClean( button );
        }
        //frame.repaint();
        isCaps = !isCaps;
    }
    protected static class ComplexButton extends JButton
    {
        public ComplexButton( String string )
        {
            super(string);
        }

        @Override
        public void paint( Graphics g )
        {
            int breakpoint = 3000000;
            super.paint( g );
            // Simulate some complex operations.
            for (int i = 0; i < breakpoint; ++i)
            {
                g.setColor( new Color( i%255, (2*i)%255, (3*i)%255 ));
            }
        }
    }
}

请注意,如果您从FormLayout更改为FlowLayout,它可以正常工作(尽管本质上是缓慢的)。如果删除注释掉的代码上的注释,它也可以正常工作(感谢MadProgrammer)。

另请注意,如果将printlns放在updateButtons()方法的开头和结尾处,该方法将在按钮停止更新之前很久结束,并且按钮不会一致更新。这意味着重绘的合并性质在某种程度上不会使用FormLayout保留。

无论如何,即使它被保留,缓慢的控制也与混乱更新控制一样糟糕。我猜我将不得不尝试优化我们的油漆代码。感谢您的支持。

1 个答案:

答案 0 :(得分:3)

Swing中的绘画由RepaintManager控制,它决定什么时候应该更新。 RepaintManager已经过优化,可以减少计划的绘制事件数量,以保持性能。

这意味着当它收到大量重绘请求时,它会将它们合并为尽可能少的绘制事件。这意味着当您在一堆按钮上的循环内调用setText时,RepaintManager可能会将此减少到尽可能接近1(取决于您在那里更新的内容)可能会更多,但很可能会少于循环的迭代次数... ...

例如,它似乎对我有用......

Keys

import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestKeyboard {

    public static void main(String[] args) {
        new TestKeyboard();
    }

    public TestKeyboard() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private List<JButton> buttons;

        public TestPane() {
            setFocusable(true);
            setLayout(new GridLayout(0, 4));
            buttons = new ArrayList<>(26);
            for (int index = 0; index < 26; index++) {
                JButton btn = createButton(index);
                buttons.add(btn);
                add(btn);
            }

            addKeyListener(new KeyAdapter() {

                @Override
                public void keyPressed(KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
                        for (JButton btn : buttons) {
                            String text = btn.getText().toUpperCase();
                            btn.setText(text);
                        }
                        revalidate();
                        repaint();
                    }
                }

                @Override
                public void keyReleased(KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
                        for (JButton btn : buttons) {
                            String text = btn.getText().toLowerCase();
                            btn.setText(text);
                        }
                        revalidate();
                        repaint();
                    }
                }               
            });
        }

        protected JButton createButton(int index) {
            JButton btn = new JButton(Character.toString((char) ('a' + index)));
            btn.setMargin(new Insets(4, 4, 4, 4));
            btn.setFocusable(false);
            btn.setFocusPainted(false);
            return btn;
        }

    }

}