📄 jv0403.htm
字号:
MediaTracker tracker;<br>
Image walkerImgs[]=new Image[5]; //存放鸵鸟走路姿势图像<br>
Image currentImg; //当前放映的鸵鸟动作图像<br>
int xpos,ypos=0; //鸵鸟动作图像显示的位置<br>
int walk_step=20; //鸵鸟图像每次移动的距离<br>
int delay = 250; //每帧时延(毫秒)<br>
Thread runThread;<br>
Image bgImage; //存放草原背景图像<br>
int applet_width,applet_height;<br>
int birdImg_width; //鸵鸟图像宽度<br>
public void init(){<br>
tracker = new MediaTracker(this);<br>
for (int i=0;i xiaoyu walkerImgs.length;i++){ //获取鸵鸟动作图像<br>
walkerImgs[i]=getImage(getCodeBase(),"images/bird"+i+".gif");<br>
tracker.addImage(walkerImgs[i], 0); //列入0组跟踪范围<br>
}<br>
bgImage = getImage(getCodeBase(),"images/"+"bg.gif"); //获取草原背景图像<br>
tracker.addImage(bgImage, 0); //列入0组跟踪范围<br>
applet_width=size().width;<br>
applet_height=size().height;<br>
}<br>
public void start (){<br>
if (runThread==null){<br>
runThread=new Thread(this);<br>
runThread.start();<br>
}<br>
}<br>
public void stop(){<br>
if (runThread!=null){<br>
runThread.stop();<br>
runThread=null;<br>
}<br>
}<br>
public void run(){<br>
try{<br>
tracker.waitForID(0);<br>
}catch(InterruptedException e){<br>
return;<br>
}<br>
birdImg_width=walkerImgs[0].getWidth(this);<br>
int i=0;<br>
while(true){<br>
for(xpos=-birdImg_width;xpos<=applet_width;xpos+=walk_step){//计算位置<br>
currentImg=walkerImgs[i];<br>
repaint();<br>
i=(i+1)%walkerImgs.length; //计算下一帧是哪幅图像<br>
try{Thread.sleep(delay);}<br>
catch(InterruptedException e){}<br>
}<br>
}<br>
}<br>
public void paint(Graphics g){<br>
g.drawImage(bgImage,0,0,this);<br>
g.drawImage(currentImg,xpos,ypos,this);<br>
}<br>
public void update(Graphics g){<br>
paint(g);<br>
}<br>
}</P>
<p> <a name="437"></a>4.3.7 双缓冲技术</P>
<p> 虽然我们在上一例鸵鸟动画中覆盖了update( )方法,阻止系统每次都用背景色去清洗屏幕这一无用的操作,但是动画效果还是不尽人意:闪烁得厉害。那既然采用了同样的方法为什么数码钟闪烁的情况要好得多呢?关键在于鸵鸟动画里的图像文件都较大,系统每次在屏幕上画起来速度就慢,闪烁当然厉害了。<br>
这一节中,我们介绍一种消除闪烁的最佳法宝--双缓冲(double-buffering)技术。说它最佳,是因为利用此技术制作出来的动画能最大限度地避免闪烁现象。但这种技术的缺点就是需要占用大量的内存。因此只有当你的动画实在为严重的闪烁问题所困扰时,才应考虑使用这种技术。由于我们已经发现,闪烁问题的根源就在于系统每次直接在屏幕上作画的速度太慢(尤其是要画的图像又大又多时),因此双缓冲技术就考虑在屏幕外面创建一个虚拟的备用屏幕,系统直接在备用屏幕上作画,等画完以后就将备用屏幕中的点阵内容直接切换给当前屏幕。这种直接切换准备好的画面页的速度与在屏幕上当场一一作画的速度相比,当然快了许多。<br>
下面,我们就来一步一步地修改上面的鸵鸟动画代码,使其实现双缓冲技术。<br>
1. 首先,我们在applet中声明两个实例变量:<br>
Image offScreenImg; //存放备用屏幕的内容<br>
Graphics offScreenG; //备用屏幕的绘图上下文环境<br>
2. 在init( )方法中加入下列语句:<br>
try {<br>
offScreenImg = createImage (applet_width,applet_height);//创建备用屏幕<br>
offScreenG = offScreenImg.getGraphics (); //获取备用屏幕的绘图上下文环境<br>
} <br>
catch (Exception e) {<br>
offScreenG = null; //若出错,就置备用屏幕的绘图上下文环境为null<br>
}<br>
其中createImage方法是定义在java.awt.Compont类中的方法,由于我们的applet是它的子类,从而就自然地继承了该方法。此方法是用来创建一个空的可以在上面进行绘图的Image对象,它的两个整型参数分别表示所创建的该Image对象的宽度和高度,我们这里就设为整个applet屏幕的宽和高,以便将它作为一个备用屏幕。紧接着,调用Image对象中的getGraphics(
)方法,它是用来获取一个可以在该Image对象上进行绘图的绘图上下文环境,也就是一个Graphics对象offScreenG,以后凡是调用offScreenG中的任何绘图方法(如drawImage(
)方法)都将作用在备用屏幕(offScreenImg)上。如果上述执行过程中发生错误,我们就将这个Graphics对象设为null值。<br>
3. 将update( )方法改为:<br>
public void update(Graphics g)<br>
{<br>
if (offScreenG!=null) { //如果备用屏幕创建成功<br>
paint(offScreenG);<br>
g.drawImage(offScreenImg,0,0,this); //将备用屏幕内容画到当前屏幕来<br>
} else<br>
paint(g);<br>
}<br>
即如果备用屏幕创建成功的话,就将备用屏幕的绘图上下文环境offScreenG传递给paint( )方法,这样一来,paint( )方法中所画的内容都将画在备用屏幕上。然后再调用drawImage(
)方法将备用屏幕offScreenImg中的内容画到当前屏幕上。当然,如果创建备用屏幕不成功的话,就和以前一样,将系统生成的当前屏幕的绘图上下文环境g传递给paint(
)方法。<br>
好了,实现双缓冲技术就是这样地简单。我们下面就列出改进后的鸵鸟动画程序的原代码。另外,我们还在其中加入了一段播放背景音乐的代码,以使这个动画现得更加有声有色。<br>
import java.awt.MediaTracker;<br>
import java.awt.Graphics;<br>
import java.awt.Image;<br>
import java.applet.AudioClip;<br>
public class Walker extends java.applet.Applet implements Runnable{<br>
Image offScreenImg; //存放备用屏幕的内容<br>
Graphics offScreenG; //备用屏幕的绘图上下文环境<br>
MediaTracker tracker; //媒体跟踪器<br>
Image walkerImgs[]=new Image[5]; //存放鸵鸟走路姿势图像<br>
Image currentImg; //当前放映的鸵鸟动作图像<br>
int xpos,ypos=0; //鸵鸟动作图像显示的位置<br>
int walk_step=20; //鸵鸟图像每次移动的距离<br>
int delay = 250; //每帧时延(毫秒)<br>
Thread runThread;<br>
Image bgImage; //存放草原背景图像<br>
int applet_width,applet_height;<br>
int birdImg_width; //鸵鸟图像宽度<br>
AudioClip bgmusic;<br>
public void init(){<br>
tracker = new MediaTracker(this);<br>
for (int i=0;i Xiaoyu walkerImgs.length;i++){ //获取鸵鸟动作图像<br>
walkerImgs[i]=getImage(getCodeBase(),"images/bird"+i+".gif");<br>
tracker.addImage(walkerImgs[i], 0); //列入0组跟踪范围<br>
}<br>
bgImage = getImage(getCodeBase(),"images/"+"bg.gif"); //获取草原背景图像<br>
tracker.addImage(bgImage, 0); //列入0组跟踪范围<br>
bgmusic=getAudioClip(getDocumentBase(),"space.au"); //获取背景音乐<br>
applet_width=size().width;<br>
applet_height=size().height;<br>
try {<br>
offScreenImg = createImage (applet_width,applet_height);//创建备用屏幕<br>
offScreenG = offScreenImg.getGraphics ();//获取备用屏幕的绘图上下文环境<br>
}<br>
catch (Exception e) {<br>
offScreenG = null; //若出错,就置备用屏幕的绘图上下文环境为null<br>
}<br>
}<br>
public void start (){<br>
if (runThread==null){<br>
runThread=new Thread(this);<br>
runThread.start();<br>
}<br>
}<br>
public void stop(){<br>
if (runThread!=null){<br>
if(bgmusic!=null)<br>
bgmusic.stop();<br>
runThread.stop();<br>
runThread=null;<br>
}<br>
}<br>
public void run(){<br>
try{<br>
tracker.waitForID(0); //等待0组中所有图像的到达<br>
}catch(InterruptedException e){<br>
return;<br>
}<br>
if(bgmusic!=null)<br>
bgmusic.loop();<br>
birdImg_width=walkerImgs[0].getWidth(this);<br>
int i=0;<br>
while(true){<br>
for(xpos=-birdImg_width;xpos<=applet_width;xpos+=walk_step){//计算位置<br>
currentImg=walkerImgs[i];<br>
repaint();<br>
i=(i+1)%walkerImgs.length; //计算下一帧是哪幅图像<br>
try{Thread.sleep(delay);}<br>
catch(InterruptedException e){}<br>
}<br>
}<br>
}<br>
public void paint(Graphics g){<br>
g.drawImage(bgImage,0,0,this);<br>
g.drawImage(currentImg,xpos,ypos,this);<br>
}<br>
public void update(Graphics g){<br>
if (offScreenG!=null) { //如果备用屏幕创建成功<br>
paint(offScreenG);<br>
g.drawImage(offScreenImg,0,0,this); //将备用屏幕内容画到当前屏幕来<br>
}<br>
else<br>
paint(g);<br>
}</P>
</BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -