В Java, как мне перерисовать панель из потока actionPerformed, пока он работает?

У меня есть класс (называемый Class_GUI), в котором есть панель с множеством кнопок. Class_GUI имеет несколько методов, которые изменяют текст и цвет кнопок.

У меня также есть программа с методом actionPerformed. Когда он вызывается, он создает экземпляр Class_GUI и многократно вызывает методы Class_GUI, изменяя кнопки и т. Д.

Проблема, с которой я столкнулся, заключается в том, что кнопки отображаются правильно только после полного завершения метода actionPerformed, тогда как я хочу, чтобы он менялся после вызова каждого метода Class_GUI.

Моя попытка до сих пор в каждом методе Class_GUI, я делаю это в конце метода:

SwingUtilities.invokeLater(Refresh_GUI);

Где Refresh_GUI определен:

Runnable Refresh_GUI = new Runnable(){
    public void run(){
        frame.revalidate();
        frame.repaint();
    }
};

4 ответа

Предполагая, что ваш actionPerformed метод вызывается в контексте потока диспетчеризации событий, обновления пользовательского интерфейса не производятся, пока ПОСЛЕ actionPerformed метод конкурировал, даже используя SwingUtilities#invokeLater не изменит это, потому что до actionPerformed При выходе из метода EDT не сможет продолжить обработку (среди прочего) запросов на перерисовку.

Лучшее, что вы можете сделать, - это запустить второй поток и внутри него обновить компоненты UI... но вы будете вынуждены использовать эту область. SwingUtilities#invokeLater как вы никогда не должны обновлять любой компонент пользовательского интерфейса за пределами EDT.

Преимущество в том, что поток не должен конкурировать, чтобы EDT начал обрабатывать запрос на перерисовку.

ОБНОВЛЕНО примером

public class SwingThreadUpdate {

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

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

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new BlinkPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class BlinkPane extends JPanel {

        private JLabel label;
        private JButton button;

        public BlinkPane() {
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridy = 0;

            label = new JLabel("Blinky");
            label.setBackground(Color.RED);
            button = new JButton("Click me");

            add(label, gbc);
            gbc.gridy++;
            add(button, gbc);

            button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    button.setEnabled(false);
                    new Thread(new BlinkTask(BlinkPane.this)).start();
                }
            });

        }

        private void setBlink(boolean blink) {
            label.setOpaque(blink);
        }

        private void reset() {
            button.setEnabled(true);
            label.setOpaque(false);
        }
    }

    public class BlinkTask implements Runnable {

        private BlinkPane blinkPane;

        protected BlinkTask(BlinkPane blinkPane) {
            this.blinkPane = blinkPane;
        }

        @Override
        public void run() {
            Blink blinkOn = new Blink(blinkPane, true);
            Blink blinkOff = new Blink(blinkPane, false);

            for (int index = 0; index < 10; index++) {
                if (index % 2 == 0) {
                    SwingUtilities.invokeLater(blinkOn);
                } else {
                    SwingUtilities.invokeLater(blinkOff);
                }
                try {
                    Thread.sleep(125);
                } catch (InterruptedException ex) {
                }
            }

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    blinkPane.reset();
                }
            });

        }
    }

    public class Blink implements Runnable {

        private BlinkPane blinkPane;
        private boolean blink;

        public Blink(BlinkPane blinkPane, boolean blink) {
            this.blinkPane = blinkPane;
            this.blink = blink;
        }

        @Override
        public void run() {
            blinkPane.setBlink(blink);
            blinkPane.repaint();
        }
    }
}

Возможно, вы захотите ознакомиться с живописью в AWT и Swing для получения дополнительной информации.

Включив код вызова метода actionPerform для обновления кнопок в цикле for, вы также можете добавить код обновления в invokeLater таким образом, чтобы как код обновления, так и код рисования выполнялись одна за другой. Invoke позже будет выполняться только после того, как текущий метод завершит свое выполнение, поэтому единственный способ обеспечить ускорение рисования - это разбить ваши задачи на более мелкие кусочки.

Во-первых, убедитесь, что вы получаете доступ к любым компонентам GUI только из потока диспетчера событий (через invokeLater или как часть обработки события GUI).

Во-вторых, если вы измените какие-либо свойства компонента GUI, он должен автоматически опубликовать событие для перерисовки. Если нет, вы можете попробовать вызвать component.repaint(), Но очень важно, чтобы изменения свойств компонента происходили в EDT.

Простое решение - выполнить всю задачу ActionPerformed без учета событий, чтобы очистить экран в конце очереди событий. Итак, сначала он выполняет функцию cleanScreen(), потому что остальная часть события ожидает завершения всех событий.

    AnyButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            cleanScreen();        //Modify components before action performer event
            EventQueue.invokeLater( new Runnable() {
                @Override public void run() {
                    anytask();    //Action performer event
                }
            });                     
        }
    });
Другие вопросы по тегам