使用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
教程包含更多信息。
-
不要扩展
JFrame
,将JFrame
创建为局部变量 -
不要使用
KeyListener
,而是使用KeyBindings
-
不要直接绘制到
JFrame
,使用drawImage()
到JLabel
或JComponent/JPanel
-
这样可以激活我的角色,但速度太快以至于我们可以看到一个东西,我该怎么做来观看动画?
KeyListener
另一个问题是,您必须设置两个KeyEvents
之间的延迟
您可以使用Swing Timer并定期更新动画。 请参见如何使用Swing Timers