java

位置:IT落伍者 >> java >> 浏览文章

优化Java动画编程中的显示效果


发布日期:2018年01月10日
 
优化Java动画编程中的显示效果

Java动画编程有多种实现方法但它们实现的基本原理是一样的即在屏幕上画出一系列的帧来造成运动的感觉Java多线程技术是Java动画编程中普遍运用的技术它在控制动画程序的流程和动画的显示效果方面起着重要的作用Java动画编程中的动画闪烁和图像残缺不全等现象是Java程序员经常遇到的问题本文以作者应用实例程序为基础阐述如何运用多线程重载Update双缓沖和图像跟蹤等技巧来解决这类问题以达到动画显示的最佳效果

Java多线程技术

Java多线程技术简介

目前线程(Thread)已经为许多操作系统和应用开发系统所采用线程是程序的单个控制流具有顺序程序的特点但是线程不是一个程序它仅仅是程序的一个执行序列线程具有很强的并发功能在同一时刻可以有多个线程同时处于执行状态线程是动态的具有一定的生命周期分别经历从创建执行阻塞直到消亡的过程Java语言对多线程编程的支持有两种实现方法一种是直接继承Thread类另一种是实现Runnable接口Thread类提供了对线程的控制方法如start()stop()run()suspend()resume()和sleep()等方法它们可以对线程的状态进行控制

动画线程的设计与实现

为了每秒中多次更新屏幕必须创建一个线程来实现动画的循环这个循环要跟蹤当前帧并响应周期性的屏幕更新要求许多Java初学者容易犯的一个错误是将动画循环放在paint()中这样占据了主AWT线程而主线程将负责所有的绘图和事件处理因此应该生成一个独立的动画线程来完成图像的显示和更新例如在一个Applet框架下当Applet启动(Start)时生成一个动画线程;在Applet停止(stop)时终止该动画线程以释放它所占用的CPU资源下列程序代码(简称C代码)是该动画线程的具体实现

public void start() {

if(animatorThread==null) {

animatorThread=new Thread(this);

//开始动画线程

animatorThreadstart();

}

}

public void stop(){

//停止动画线程

animatorThread=null;

}

上面终止动画线程的时候并不是调用该动画线程的stop()方法而是设置该动画线程为null因为如果直接调用线程的stop()方法会强制线程终止所有的执行工作有时会带来不好的结果设置该动画线程为null则在run()方法中由于不满足循环条件线程会自然退出这样也进一步优化了该动画程序

重载update()和双缓沖技术消除闪烁

在Java中动画发生闪烁有两个原因一个是由于在显示下一帧画面的时候调用了repaint()方法而repaint()方法被调用时要清除整个背景然后才调用paint()方法显示画面这样在清除背景和绘制图像的短暂时间间隔内被用户看见的就是闪烁另一个是由于paint()方法要进行复杂的计算绘制每一帧花费的时间太长图像中的各个像素值不能同时得到使得动画的生成频率低于显示器的刷新频率从而造成闪烁

下面两种方法可以明显地消除或减弱闪烁

重载update()方法

当AWT接收到一个Applet的重绘请求时它就调用Applet的update()方法缺省情况下update()方法清除Applet的背景然后调用paint()方法重载update()方法就可以将以前在paint()方法中的绘图代码包含在update()方法中从而避免每次重绘时将整个区域清除既然背景不再自动清除Java程序员需要自己在update()中完成

双缓沖技术

另一种消除帧之间闪烁的方法是使用双缓沖技术它在许多动画Applet中被使用主要原理是创建一幅后台图像将每一帧画入图像然后调用drawImage()方法将整个后台图像一次画到屏幕上去这种方法的优点在于大部分绘制是离屏的将离屏图像一次绘至屏幕上比直接在屏幕上绘制要有效得多在创建后台图像前首先要通过调用createImage()方法生成合适的后台缓沖区然后获得在缓沖区做图的环境(即Graphics类对象)

下列实例程序代码(简称C代码)就是这两种方法的结合使用双缓沖技术在重载update()方法中实现其中offImage是Image类的对象offGraphics是Graphics类的对象这两个类对象是实现双缓沖技术的关键相关代码如下

public void paint(Graphics g){

update(g);

}

public void update(Graphics g){

Dimension d=getSize();

//如果后台图像不存在就创建一个后台图像

if((offGraphics==null)||(dwidth!=offDimensionwidth)

  ||(dheight!=offDimensionheight)) {

offDimension=d;

offImage=createImage(dwidthdheight);

offGraphics=offImagegetGraphics();

}

//擦除上一帧

offGraphicssetColor(getBackground());

offGraphicsfillRect(dwidthdheight);

offGraphicssetColor(Colorblack);

//将当前的帧输出到指定的image中

for(int i= ; i<10 ; i++){

       offGraphics.drawImage(images[i],frameNumber*5%(d.width/2)

                          ,i*d.height/10,this);

}

//输出指定的后台图像

g.drawImage(offImage,frameNumber*5%(d.width/2),0,this);

}

双缓沖技术可以使动画平滑,但有一个缺点,要分配一个后台图像的缓沖,如果图像相当大,这将占用很大一块内存。tW.wINgWIt.cOM

图像跟蹤与程序的逐步完善

图像跟蹤

当动画线程刚刚启动的时候,由于没有全部载入图像,屏幕上显示的画面经常是残缺不全的。这时可以使用MediaTracker或ImageOberver类对象进行图像跟蹤,待图像全部载入后,再调用drawImage()方法将图像输出到屏幕上去。DrawImage()方法的第四个参数正是ImageObserver类对象,所以可以用ImageObserver类对象进行图像跟蹤。在实际应用Applet程序的init()方法中实现图像跟蹤,相当于在动画线程的DrawImage()方法调用以前就画了一次图像,因为动画线程的初始化过程,即init()方法是先被调用的。下列代码(简称“C3”代码)展示了init()方法使用MediaTracker类对象来实现跟蹤图像的载入,代码如下:

public void init(){

tracker=new MidiaTracker(this);

for(int i=1;i<=10;i++){

  image[i-1]=getImage(getCodeBase(),"image"+i+" .gif");

  //用MediaTracker类对象的addImage()方法跟蹤图像的载入

  tracker.addImage(images[i-1],0);

}

......

}

程序的进一步完善

在“C2”代码的重载update()方法中加入下列if语句,从而对MediaTracker类对象的图像跟蹤方法做出判断,if语句如下:

if(!tracker.checkAll()){

//如果图像还没有装载完毕,则仅清除背景,同时输出一个状态

  g.clearRect(0,0,d.width,d.height);

  g.drawString("Please wait...",0,d.height/2);

  return;

}

在“C1”代码的stop()方法中加入两行代码,用以释放由双缓沖技术所占用的内存资源,这时stop()方法改为:

public void stop(){

//停止动画线程

animatorThread=null;

//释放用于双缓沖的内存资源

offGraphics=null;

offImage=null;

}

程序修改到此,还有一个小问题,就是动画线程启动后,第一幅图像有时仍有残留痕迹,而不是随着图像的更新而完全擦除掉。如果想解决此问题,只要将“C2”代码中最后的for()循环和g.drawImage()方法改为如下代码就可以了。

for(int i=0;i<10;i++){

  offGraphics.drawImage(images[frameNumber%10],

     ,frameNumber*5%(d.width),i*d.height/10,this);

}

g.drawImage(offImage,0,0,this);

保持恆定的帧速度

为了使用户观看动画时没有闪烁感,至少需要达到每秒12帧的速度。更高的帧速度会产生更平滑的动画。通常,在动画显示的每两帧之间,调用线程的sleep()方法休眠一个固定的时间。这样做的缺点是使用绝对的延迟时间会使延迟过长,即造成等待时间过长。为了每秒显示10~20帧图像,并保持恆定的帧速度(也就是恆定的显示频率),在动画线程的run()方法中可加入如下代码:

try{

startTime+=delay;

Thread.sleep(Math.max(0,startTime-System.currentTimeMillis()));

}catch(InterruptedException e){

break;

}

本文着重阐述了优化Java动画编程的显示效果的几种方法。当然,随着Java技术以及其它计算机技术的发展,还将会有多种优化动画显示效果的方法,例如,在提高动画的帧速度方面,可在Java中调用微软公司的DirectDraw工具。但是本文所介绍的方法更具有通用性。

               

上一篇:JAVA基本名词解释

下一篇:漫谈Java平台上的CRM系统