📄 jv0403.htm
字号:
paint(g);<br>
}<br>
好了,下面我们就列出该数码钟的全部代码:<br>
import java.awt.Color;<br>
import java.awt.Graphics;<br>
import java.awt.Image;<br>
import java.util.Date;<br>
public class DigClk extends java.applet.Applet implements Runnable<br>
{<br>
Thread timer = null;<br>
Image[] digit_image = new Image[10]; // 数码(0-9)图像数组<br>
Image colon_image, // 冒号图像<br>
frame_image; // 边框图像<br>
int digit_height = 21; // 数码(及冒号)高度<br>
int digit_width = 16; // 数码宽度<br>
int colon_width = 9; // 冒号宽度<br>
int offset = 4; // 边框厚度<br>
int applet_width;<br>
int applet_height;<br>
int[] image_start_x = new int[8]; // 数码或冒号的水平起始位置数组<br>
public void init()<br>
{<br>
for (int i = 0; i < 10; i++){<br>
digit_image[i] = getImage(getCodeBase(), "dcimages/lcd" + i + ".gif");<br>
}<br>
colon_image = getImage(getCodeBase(), "dcimages/colon.gif");<br>
frame_image = getImage(getCodeBase(), "dcimages/frame.gif");<br>
applet_width = (2 * offset) + (6 * digit_width) + (2 * colon_width);//计算applet宽度<br>
applet_height = (2 * offset) + (digit_height); //计算applet高度<br>
image_start_x[0] = offset; // 填充起始位置数组<br>
for (int i = 1; i < 8; i++){ <br>
if ((i = = 3) || (i = = 6)) // 下一位置是冒号<br>
image_start_x[i] = image_start_x[i - 1] + colon_width;<br>
else // 下一位置是数码<br>
image_start_x[i] = image_start_x[i - 1] + digit_width;<br>
}<br>
}<br>
public void start()<br>
{<br>
if (timer == null){<br>
timer = new Thread(this);<br>
timer.start();<br>
}<br>
}<br>
public void run()<br>
{<br>
while (timer != null){<br>
try{<br>
timer.sleep(1000); //1秒延时<br>
}catch (InterruptedException e){ }<br>
repaint();<br>
}<br>
}<br>
public void stop()<br>
{<br>
if (timer != null){<br>
timer.stop();<br>
timer = null;<br>
}<br>
}<br>
public void paint(Graphics g)<br>
{<br>
Date now = new Date(); // 获取当前日期和时间的对象<br>
int hour = now.getHours(); // 取小时数<br>
int minute = now.getMinutes(); // 取分钟数<br>
int second = now.getSeconds(); // 取秒钟数<br>
int i = 0; // 水平起始位置数组的索引<br>
g.drawImage(frame_image, 0, 0, this);<br>
g.drawImage(digit_image[hour / 10], image_start_x[i++], offset, this);<br>
g.drawImage(digit_image[hour % 10], image_start_x[i++], offset, this);<br>
g.drawImage(colon_image, image_start_x[i++], offset, this);<br>
g.drawImage(digit_image[minute / 10], image_start_x[i++], offset, this);<br>
g.drawImage(digit_image[minute % 10], image_start_x[i++], offset, this);<br>
g.drawImage(colon_image, image_start_x[i++], offset, this);<br>
g.drawImage(digit_image[second / 10], image_start_x[i++], offset, this);<br>
g.drawImage(digit_image[second % 10], image_start_x[i], offset, this);<br>
}<br>
public void update(Graphics g)<br>
{<br>
paint(g);<br>
}<br>
}</P>
<p> <a name="435"></a>4.3.5 使用媒体跟踪器</P>
<p> 如果我们真的上机运行上面的数码钟实例时,准会被动画刚开始运行时的画面乱动现象吓一跳:各图片并非一下子显示出来,而都从顶部开始参差不齐地慢慢显示,同时整个画面都伴随着强烈的闪烁,如图4-18所示。直至画面全部显示完整后,才恢复正常。</P>
<p> <IMG
height=99 src="tu4-19.gif" width=130></P>
<p> 图4-18 数码钟刚开始运行时的画面乱动现象<br>
这是为什么呢?显然这是因为图像数据尚未完全准备好就迫不急待地开始显示了,当然在开始时只能显示一小半画面了。那我们在init( )方法里不是用getImage(
)方法把所有图像都装载进来了吗?原来,getImage( )方法被调用时,仅仅是立刻生成一个Image对象返回给调用者,但这并不表示图像文件已经被装载到内存中,而是与此同时,系统马上产生了另一个线程去真正读取该图像文件的数据。因而,往往程序已经运行到getImage(
)后面的语句时,系统却还正在装载图像文件的数据,尤其是从网络上装载图像文件时,这种情况会更加严重。那么怎样才能让图像文件的数据都到齐了才播映呢?Java提供了一个java.awt.MediaTracker类,可用它来跟踪媒体装载的情况,其构造方法的调用格式为:<br>
MediaTracker(Component comp)<br>
它需要一个Component对象作为参数,来表明此媒体跟踪器是为谁服务的,我们就自然用this作为参数值(因为我们的applet正是java.awt.Component类中的一个子类)。一旦创建了一个MediaTracker对象后,我们就可以调用它的addImage(
)方法将某Image对象列入跟踪监控的范围,其调用格式如下:<br>
void addImage(Image image, int id) <br>
这里的第一个参数表示需监控的Image对象,第二个参数则表示这个Image对象所属的被监控组的编号。将该Image对象列入跟踪范围后,我们就可以在开始放映动画前调用MediaTracker类提供的waitForID(
)方法来等待图像数据的全部到达。该方法的调用格式如下:<br>
void waitForID(int id)<br>
这里的id参数就是需等待的图像组的编号,一旦调用了该方法,系统就会一直在这里等待,直到该组中所有图像文件的数据全部装载好以后,才会去执行下一条语句。<br>
好了,下面我们就来一步一步地修改数据钟的代码,使其加入媒体跟踪器的功能:<br>
1. 程序头部加入:<br>
import java.awt.MediaTracker;<br>
2. 在applet中加入一个实例变量:<br>
MediaTracker tracker;<br>
3. 将init( )方法修改为:<br>
public void init()<br>
{<br>
tracker = new MediaTracker(this); //创建媒体跟踪器<br>
for (int i = 0; i < 10; i++){<br>
digit_image[i] = getImage(getCodeBase(), "dcimages/lcd" + i + ".gif");<br>
tracker.addImage(digit_image[i], 0); //将该图像列入第0组跟踪范围<br>
}<br>
colon_image = getImage(getCodeBase(), "dcimages/colon.gif");<br>
tracker.addImage(colon_image, 0);<br>
frame_image = getImage(getCodeBase(), "dcimages/frame.gif");<br>
tracker.addImage(frame_image, 0);<br>
applet_width = (2 * offset) + (6 * digit_width) + (2 * colon_width);//计算applet宽度<br>
applet_height = (2 * offset) + (digit_height); //计算applet高度<br>
image_start_x[0] = offset; // 填充起始位置数组<br>
for (int i = 1; i < 8; i++){ <br>
if ((i = = 3) || (i = = 6)) // 下一位置是冒号<br>
image_start_x[i] = image_start_x[i - 1] + colon_width;<br>
else // 下一位置是数码<br>
image_start_x[i] = image_start_x[i - 1] + digit_width;<br>
}<br>
}<br>
4. 将run( )方法修改为:<br>
public void run()<br>
{<br>
try{<br>
tracker.waitForID(0); //等待0组图像媒体数据的到达<br>
}catch(InterruptedException e){<br>
return; //若发生异常就马上返回<br>
}<br>
while (timer != null){<br>
try{<br>
timer.sleep(1000); //1秒延时<br>
}catch (InterruptedException e){ }<br>
repaint();<br>
}<br>
}<br>
<a name="436"></a>4.3.6 移动型动画<br>
在这一小节中我们来尝试另一种类型的动画,那就是动画中的物体在进行各种动作同时还不断地改变自身所处的位置,也就是所谓的移动型动画。</P>
<p> <IMG
height=105 src="tu4-20.gif" width=500></P>
<p> 图4-19 鸵鸟走路姿势的图片例如,我们接下来要做的一个实例是一只快乐的鸵鸟在美丽的大草原上散步,它循环不断的从屏幕左端走入,再从屏幕的右端走出。这里我们先准备了五幅鸵鸟走路姿势的图片,如图4-19所示。另外,还有一张草原背景图片,如图4-20所示。</P>
<p> <IMG
height=142 src="tu4-21.gif" width=450></P>
<p> 图4-20 草原背景图<br>
其实,移动型动画的制作也很简单,只不过除了需要知道当前采用哪一幅动作图像以外,还需要给出物体当前应处的位置。例如,我们把所有的鸵鸟动作图像都存放在一个Image数组walkerImgs中,还用了一个整型实例变量xpos来记住鸵鸟当前水平方向的X坐标值,而当前所选的动作图像则由Image类的对象currentImg来指示。并且,xpos与currentImg的值均在run(
)方法中事先计算好,如:<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>
这里xpos的初值不取0而取为负的鸵鸟图宽度值birdImg_width,只是为了让鸵鸟能从屏幕左侧自然的切入,不造成整幅图像突然出现在屏幕左侧的感觉。walk_step实例变量表示图像每次移动的距离,delay实例变量用来控制走路节奏的快慢。而在paint(
)方法中我们必须先画上草原背景图像,然后再把鸵鸟图像覆盖上去。由于每一次鸵鸟图像放置的位置都不相同,也就等于每次都把背景图像给破坏了,所以背景图像也必须每次都重画。因而paint(
)方法只需写为:<br>
public void paint(Graphics g){<br>
g.drawImage(bgImage,0,0,this);<br>
g.drawImage(currentImg,xpos,ypos,this);<br>
}<br>
这里还有一个小小的问题,那就是一般图像都是以矩形的状态展现在我们面前,如果我们直接将鸵鸟图像贴在草原背景上,如图4-21所示,那鸵鸟的白色矩形背景与草原背景相互冲突,完全破坏了整个动画的意境。那有什么办法能将这块白色背景去掉,只留下一个活生生的鸵鸟图案呢?幸好GIF图像格式的89a版本支持一种透明背景技术,它能清除整幅图像的矩形背景,使其变为透明背景。因此必须先将上述的鸵鸟图片都转换为透明背景图,然后再贴到草原背景上,那动画效果就显得天衣无缝了,如图4-22所示。能制作透明背景图的软件有GIFTRANS及GIF
CONSTRUCTION SET等。</P>
<p> <IMG height=204 src="tu4-22.gif"
width=450></P>
<p> 图4-21 鸵鸟图像的白色背景与草原背景图相互冲突</P>
<p> <IMG height=204
src="tu4-23.gif" width=450></P>
<p> 图4-22 将鸵鸟图像制成透明背景图<br>
下面,我们就列出本动画实例的全部代码:<br>
import java.awt.MediaTracker;<br>
import java.awt.Graphics;<br>
import java.awt.Image;<br>
public class Walker extends java.applet.Applet implements Runnable{<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -