Swing事件处理程序中无闪烁的JComponent渲染

4
我正在编写一个简单的Java Swing应用程序,旨在作为课程演示。该程序的目的是可视化递归方法的进度(具体来说,是迷宫的递归解决方案)。我希望能够在递归方法执行时更新JComponent的外观,因此从Swing的角度来看,更新不能通过Timer类单独触发,这是动画的常规方法。
我的第一个想法是每当我想重新绘制可视化时调用repaint()。但是,递归方法本身是在JButton的事件处理程序中调用的,这意味着所有对repaint()的调用都延迟到事件处理程序终止之前 - 而我所看到的只是动画的最后一帧。我发现如果我改为调用update(),就可以立即进行绘制,像这样:
getRootPane().getContentPane().update(getGraphics());
try {
    Thread.sleep(25);
} catch (Exception e) {
}

这个方法基本可行,但动画会闪烁。我知道Swing是双缓冲的,但getGraphics显然给了我活动缓冲而不是后台缓冲。我找不到任何方法可以让我访问后台缓冲。
因此,我正在寻找一种解决方案,允许我在递归过程中重新绘制组件,并提供无闪烁的动画。有没有办法在事件处理程序执行时激活内置的双缓冲?尝试实现自己的缓冲系统是否更好?还是说用Swing做这个事情可能并不可行/明智?
(最后,这是我的第一篇帖子。我已经尽力写好问题,但如果有什么我漏掉的,请友善地告诉我,我下次会做得更好。谢谢!)

我认为你已经包含了所有的内容 :) 尽管看到递归可能有帮助,以便找出可以插入repaint()的位置。您可能会在递归和/或repaint中使用一些条件语句,以便仅在适当时调用它。 - CodeGuy
为什么不保留对先前缓冲区的引用并进行更新呢? - Redandwhite
2个回答

2
首先,你不应该在事件分派线程中执行长时间运行的操作,我想这可能是你的动画闪烁的原因之一。
因此, 我建议在按下按钮时启动一个执行递归方法的线程(并且可能在工作完成前禁用它)。
为了渲染动画,你可以从递归方法内部调用`myComponent.repaint()`或者启动一个Swing定时器(感谢@mKorbel提供的信息),它看起来基本上像这样:
ActionListener repaintTrigger = new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
          myComponent.repaint();
      }
};
new Timer(25, repaintTrigger).start();

我建议采用后一种方法,因为你的递归方法可能不应该知道任何关于 Swing 组件的信息(除非你通过“ProgressListener”或类似方式解耦它)。
既然你似乎不是一个纯初学者,我只是给了一些提示。如果你需要更多的帮助,请随时提问。

谢谢,这足以让我继续下去了。关键的洞察力是在另一个线程上运行递归将允许正常使用repaint()方法。我也喜欢你的其他建议,尽管我试图保持其余代码简单,以便递归仍然是重点。 - nhowe
不要使用"not"和"never"(可以使用"sure could be works"),而是使用Swing Timer来实现今天的Java6/7。 - mKorbel
@mKorbel 这些年没怎么做 Swing,感谢您指向 javax.swing.Timer - skirsch

0

我其实不知道这是否有效,但是我会尝试将其多线程化,并将递归与图形分离。我不知道这在Java上是否有效,但在C#中对我有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接