我想从我的控制器更新主框架(在类GUI中)的面板。基本上,应该在每个模拟步骤更新面板。
for (int step = 0; step < simSteps; step++){
this.gui.updateGUI(this.stats, step);
}
在我的GUI类中,方法updateGUI(...)如下所示:
public void updateGUI(Stats stats, int step){
this.stats = stats;
getContentPane().remove(pollutionPanel);
pollutionPanel = new BarPanel(settings, stats, possibleColors);
getContentPane().add(pollutionPanel);
validate();
repaint();
}
我的BarPanel类重写了paintComponent方法,我只是使用图形对象来绘制一些东西。 在开始模拟时,面板仅在最后一个模拟步骤之后更新,尽管我已经知道在每个模拟步骤中都调用了updateGUI()方法(我做了一些调试输出)。
有谁知道为什么会这样?
答案 0 :(得分:2)
所以,基本概念,你想要生成一些动画,其中UI在一段时间内更新。这不是一个不常见的请求,有很多例子。
要记住的事情:
好的,这听起来有点矛盾,但Swing提供了一些工具让您的生活更轻松
你可以......
SwingWorker
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
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.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface StepContainer {
public void addStep(int index);
}
public class TestPane extends JPanel implements StepContainer {
private int step = 0;
public TestPane() {
setLayout(new GridBagLayout());
PopulateWorker worker = new PopulateWorker(10, this);
worker.execute();
}
@Override
public void addStep(int step) {
JButton btn = new JButton(Integer.toString(step));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(btn, gbc);
revalidate();
repaint();
}
}
public class PopulateWorker extends SwingWorker<Void, Integer> {
private int steps;
private StepContainer parent;
public PopulateWorker(int steps, StepContainer parent) {
this.steps = steps;
this.parent = parent;
}
@Override
protected Void doInBackground() throws Exception {
Thread.sleep(1000);
for (int index = 0; index < steps; index++) {
publish(index);
Thread.sleep(1000);
}
return null;
}
@Override
protected void process(List<Integer> chunks) {
for (int step : chunks) {
parent.addStep(step);
}
}
}
}
Timer
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
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.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface StepContainer {
public void addStep(int index);
}
public class TestPane extends JPanel implements StepContainer {
private int step = 0;
public TestPane() {
setLayout(new GridBagLayout());
Timer timer = new Timer(1000, new PopulateAction(10, this));
timer.start();
}
@Override
public void addStep(int step) {
JButton btn = new JButton(Integer.toString(step));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(btn, gbc);
revalidate();
repaint();
}
}
public class PopulateAction implements ActionListener {
private int steps;
private StepContainer parent;
private int step;
public PopulateAction(int steps, StepContainer parent) {
this.steps = steps;
this.parent = parent;
}
@Override
public void actionPerformed(ActionEvent e) {
parent.addStep(step);
if (step == steps - 1) {
((Timer) e.getSource()).stop();
step = 0;
}
step++;
}
}
}
您使用的将取决于问题的复杂程度,SwingWorker
使用它自己的Thread
来调用doInBackground
,允许您处理耗时的任务,但提供能够安全地更新UI。
Timer
在EDT的上下文中触发它的更新,使其保存以更新UI,但不执行耗时的任务
答案 1 :(得分:0)
如果您非常快速地更新GUI,则看不到中间更改,因此在循环中的每个步骤之间添加一些暂停,如:
new Thread() {
public void run() {
for (int step = 0; step < simSteps; step++){
this.gui.updateGUI(this.stats, step);
try {
Thread.currentThread().sleep(1000); // wait 1s before each step
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
public void updateGUI(final Stats stats, final int step){
// the declaration of stats in this class should be marked as volatile like:
// private volatile Stats;
this.stats = stats;
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
getContentPane().remove(pollutionPanel);
pollutionPanel = new BarPanel(settings, stats, possibleColors);
getContentPane().add(pollutionPanel);
validate();
repaint();
}
});
}
还要记住,上面的这个循环不应该由EventThread
运行,所以你不应该直接在ActionListener
中运行这段代码(例如在按下JButton
之后),但是而是在ActionListener.actionPerformed()
创建new Thread
中,其中包含run()
方法中的上述代码,然后使用yourThread.start()
启动此类线程。您也可以使用其他方式在单独的线程中运行它,但它太宽泛,不适合您的问题范围。