从Controller方法更新面板

时间:2017-02-08 11:04:31

标签: java swing jpanel

我想从我的控制器更新主框架(在类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()方法(我做了一些调试输出)。

有谁知道为什么会这样?

2 个答案:

答案 0 :(得分:2)

所以,基本概念,你想要生成一些动画,其中UI在一段时间内更新。这不是一个不常见的请求,有很多例子。

要记住的事情:

  • Swing是单线程的
  • 不要做任何可能会阻止事件调度线程的事情,这会阻止UI更新,直到控件传回给它为止
  • 不要在EDT上下文之外更新用户界面

好的,这听起来有点矛盾,但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);
            }
        }

    }

}

使用Swing 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()启动此类线程。您也可以使用其他方式在单独的线程中运行它,但它太宽泛,不适合您的问题范围。