在多线程环境中重新绘制

时间:2011-05-24 14:55:22

标签: java multithreading swing applet event-dispatch-thread

我正在开发一个包含大约十个不同数据源的applet(例如statistics / error-log / ...)。每个数据源由单个网络连接更新,并通过观察器机制报告更新。 applet具有不同的视图,可以显示部分数据。每个视图只对数据的某些部分感兴趣,并在必要的Observables中将其自身注册为Observer。

视图(扩展的JPanel)主要由标准的swing组件组成(例如JLabel,JButton,......)。视图中组件的某些属性取决于基础数据模型中的信息。

示例:

StatisticPanel::paintComponent(Graphics g) {
  clearStatisticButton.setEnabled(stat.hasEntries());
  minValueLabel.setText(stat.getMinValue());
  super.paintComponent(g);
}

此逻辑在StatisticPanel的paintComponent()方法中实现,update()方法只调用repaint(),因为我不希望操作EDT之外的组件。

这是在多线程环境中更新swing组件的预期方法吗?使用SwingUtitlies.invokeLater()的Runnable更好吗?这个问题有更好的方法吗?

2 个答案:

答案 0 :(得分:6)

我是第二个camickr的建议,但关于这段代码:

StatisticPanel::paintComponent(Graphics g) {
  clearStatisticButton.setEnabled(stat.hasEntries());
  minValueLabel.setText(stat.getMinValue());
  super.paintComponent(g);
}

你的paintComponent方法中有非绘画方法(前两种方法),这不应该是1)你希望这种方法尽可能精简和快速,因此只有绘画相关的代码, 2)你没有aboslute控制何时调用此方法或甚至调用它,因此非绘制相关的代码和程序逻辑不属于那里。出于这些原因,我强烈建议你把它们从那里取出来,但是应该与paintComponent分开调用,但与大多数Swing代码一样,在EDT上。

编辑1
我不是专业人士,但如果你给StaticPanel一个类似的方法怎么样:

   public void doMyUpdate() {
      if (SwingUtilities.isEventDispatchThread()) {
         clearStatisticButton.setEnabled(stat.hasEntries());
         minValueLabel.setText(stat.getMinValue());
      } else {
         SwingUtilities.invokeLater(new Runnable() {
            public void run() {
               clearStatisticButton.setEnabled(stat.hasEntries());
               minValueLabel.setText(stat.getMinValue());
            }
         });
      }
      repaint(); // if the paintComponent method has more than just a super call.
   }

编辑2
另外,请看一下这个主题:check-if-thread-is-edt-is-necessary

答案 1 :(得分:4)

repaint()用于调用Swing RepaintManger,后者将调度重新绘制组件,所以是的,可以直接调用repaint。 RepaintManager将确保所有重新绘制都在EDT上完成。