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