项目场景:
在做贪吃蛇游戏时,加了个通关切换效果,用要延时技术!发现使用repaint方法+Thread.sleep()或repaint方法+Timer均可达到目的,但其中走了不少弯路,有必要将两种方式做个分析总结。
问题描述
问题代码示例一:延迟调用repaint()
public void test1(){
try {
Thread.sleep(1000);//延迟1秒执行
} catch (InterruptedException e) {
e.printStackTrace();
}
repaint();
}
问题代码示例二:循环调用repaint()
public void test2(){
for(int i=0;i<10;i++){
repaint();
}
}
上述代码中,示例一直接休眠当前线程,会出现卡顿显现;示例二,只能得到最后一次调用repaint()结果。是什么原因造成的?如何解决呢?
原因分析:
实际上,java在处理GUI任务时候会生成唯一线程用于管理分发事件,这个线程学名叫EDT(Event Dispatch Thread) 绘图事件派发线程,说白了,这个东西就是管着一堆事件。OK, repaint方法可以看作一个事件,因为你在调用repaint时候,系统不会立即执行paint()方法,而是把它加入到EDT中,等待执行。所以,我们在加入很多repaint()时,最终会合并一个只能看到最后结果的事件,或者主程序休眠,GUI程序也会休眠,再执行repaint()就会卡顿。因此,解决上述示例问题最好方法是给repaint()单独开线程或通过Timer的定时任务解决(实际也是单独一个线程)。
解决方案:
以问题示例一来说,延时1秒后执行,两种方式都要重写run()方法,具体解决方案如下
1、单独开线程实现:
public void test1() { Thread newThread=new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //放要延迟执行的代码 repaint(); } }); newThread.start(); }
2、通过定时器实现
public void test1() { Timer timer=new Timer(); timer.schedule(new TimerTask() { @Override public void run() { //这里放延时执行的代码 repaint(); this.cancel(); } }, 1000, 0);//延时1秒执行 }
问题示例二与示例一解决类似,当然有些情况下,repaint()一次调用paint()完成循环,要比循环repaint()调用paint()更好些!
最后,repaint()方法在延迟后或循环中要想调到paint(),是需要单独建立通道,吃小灶的!