使用Swing动画进行计时

我有一些Swing和动画角色的问题,我有一个带有键监听器的JFrame,当用户按下时,它在这里调用我的JPanel方法

for(int i=1;i<4;i++) { pY+=16; g.drawImage(perso,pX,pY,pX+50,pY+50,0+50*i,0,50+50*i,50,this this.repaint(); } 

这样可以激活我的角色,但速度太快以至于我们可以看到一个东西,我该怎么做来观看动画?

Jonas已经给出了答案(使用Swing计时器),但是解释为什么你没有看到动画可能是有用的,以及为什么计时器是这个问题的最佳解决方案。

为什么我看不到不同的重绘

当您调用JComponent#repaint ,不重新JComponent#repaint JComponent 。 相反,在EDT上安排重新绘制某个组件的异步请求。 如果您调用许多repaint调用的调用,Swing可能会决定对这些请求进行分组并重新刷新组件一次。

我没有立即在Oracle文档中找到这方面的官方参考( Swing绘画文章似乎没有提到它)。 我发现这个的唯一地方是在本文的一个注释中,但我很确定这是在某处记录的。

为什么使用Timer是最佳解决方案

对于动画,你基本上想说:

我的角色应该在y毫秒内移动x像素

最好是,您希望在屏幕上拥有流畅的动画,因此您需要经常重新绘制。 如果你记住这一点

  • 所有与Swing组件的交互都应该在EDT上进行(事件调度线程,有关更多信息,请参阅Swutch中的Concurrency文章)
  • 你永远不应该阻止EDT,因为这会冻结你的UI,这意味着你不能在EDT中“等待”,直到重绘完成或重绘永远不会发生
  • 重绘请求可以分组,因此调用重绘x次并不能保证您的paint方法也被调用x

克服此限制的解决方案是使用Timer 。 使用相同的示例(在屏幕上移动字符),您可以使用Timer更新角色的位置并安排重新绘制。 由于在EDT上触发了Timer代码,因此不违反Swing线程规则。

在组件的paintComponent方法中,然后在当前位置绘制字符。 这可能是“先前位置+ 1”,或“前一个位置+2”(或……),具体取决于在上一次paint调用和当前paint调用之间触发Timer次数。 这可确保角色移动的速度与系统无关。 只有动画的平滑度取决于您的系统(如:有多少重绘请求被分组)。

Jonas已经链接的Swing Timer教程包含更多信息。

  1. 不要扩展JFrame ,将JFrame创建为局部变量

  2. 不要使用KeyListener ,而是使用KeyBindings

  3. 不要直接绘制到JFrame ,使用drawImage()JLabelJComponent/JPanel

  4. 这样可以激活我的角色,但速度太快以至于我们可以看到一个东西,我该怎么做来观看动画?

KeyListener另一个问题是,您必须设置两个KeyEvents之间的延迟

您可以使用Swing Timer并定期更新动画。 请参见如何使用Swing Timers