Пакетное перекрашивание компонентов Swing с использованием FormLayout

Я делаю клавиатуру с сенсорным экраном. Я хочу, чтобы буквенные клавиши изменяли текст, когда я нажимаю клавишу 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 String.

Проблема в том, что кнопки обновляются хаотично. Я понимаю, почему это происходит; когда я вызываю setText(), он вызывает repaint() для отдельного компонента, и это происходит для каждой кнопки. Мой вопрос заключается в том, могу ли я "перекрасить" эти кнопки в пакетном режиме, не подрывая дизайн Swing (я бы предпочел не переопределять метод setText () AbstractButton). Благодарю.

ОБНОВИТЬ

Оказывается, это проблема с 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 ответ

Решение

Живопись в Свинге контролируется RepaintManager который решает, когда все должно быть обновлено. RepaintManager оптимизирован для уменьшения количества расписаний событий, которые он планирует для поддержания производительности.

Это означает, что когда он получит кучу запросов на перерисовки, он объединит их в минимальное количество событий рисования. Это означает, что когда вы звоните setText из цикла на связке кнопок, вероятно, что RepaintManager уменьшил это значение настолько близко к единице, насколько это возможно (в зависимости от того, что вы обновляете, их может быть больше, но, скорее всего, оно будет меньше числа итераций цикла)...

Например, мне кажется, что это работает...

Ключи

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

    }

}
Другие вопросы по тегам