第4章 Java对多媒体的支持.docx

上传人:b****5 文档编号:12305390 上传时间:2023-04-18 格式:DOCX 页数:32 大小:162.81KB
下载 相关 举报
第4章 Java对多媒体的支持.docx_第1页
第1页 / 共32页
第4章 Java对多媒体的支持.docx_第2页
第2页 / 共32页
第4章 Java对多媒体的支持.docx_第3页
第3页 / 共32页
第4章 Java对多媒体的支持.docx_第4页
第4页 / 共32页
第4章 Java对多媒体的支持.docx_第5页
第5页 / 共32页
点击查看更多>>
下载资源
资源描述

第4章 Java对多媒体的支持.docx

《第4章 Java对多媒体的支持.docx》由会员分享,可在线阅读,更多相关《第4章 Java对多媒体的支持.docx(32页珍藏版)》请在冰豆网上搜索。

第4章 Java对多媒体的支持.docx

第4章Java对多媒体的支持

第4章Java对多媒体的支持

 

4.3动画制作

初步掌握了在Java中处理各种媒体的基本技能后,我们接下来要涉及的将是Java多媒

体世界中最吸引人,最精彩的一部分--Java动画技术。

这一节里,就让我们一起来由

浅入深地制作几个动画实例,并通过这些实例引出一系列措施用以改进动画的显示效果

,直至真正掌握Java动画技术的关键。

4.3.1一个简单实例

其实,Java的动画原理也是很简单的,首先在屏幕上显示动画的第一帧(也就是第一

幅画面),然后每隔很短的时间再显示另外一帧,如此往复。

由于人眼的视觉暂停而感

觉好象画面中的物体在运动。

我们已经掌握了用paint()方法去显示静态帧的技能,接下

来的问题就是如何不断地替换上其它帧画面。

我们可以发现,当用paint()方法在屏幕上画好一帧画面时,再用鼠标拖动这个窗口

,或用其它窗口覆盖它再移开时,这帧画面并未被破坏,而是很快地就被重新画好了。

来,是系统发现屏幕上该区域的画面已遭破坏,就自动地再一次调用paint()方法将该画

面恢复。

说得更确切一些,系统其实是去调用repaint()方法来完成重画任务,而repaint

()方法又去直接调用了update()方法,update()方法则先清除整个applet区域里的内

容,然后再调用paint()方法,从而完成了一次重画工作。

至此,我们似乎应该可以确定制作动画的基本方案了,那便是在applet开始运行后

(即start()方法中),每隔一段时间就取调用repaint()方法来重画某一帧,而paint()

方法中只需将相应的帧画到屏幕上。

这一方案是否正确呢?

我们就先来做一个简单实例试

试看吧。

这个实例很简单,是在applet中显示一行欢迎标题"Welcometohere!

"。

与以前不同

的是,这行标题并不是一下子显示出来,而是象打字一般,一个个字母跳出来,然后全部

隐去,再重复刚才的打字效果。

用动画的术语来说,第一帧显示空白,第二帧显示"W",

第三帧显示"We",直至最后一帧显示完整个字符串后,再循环到第一帧。

根据上述提供的

制作方案,我们很快就可以写出下面的程序:

1:

importjava.awt.Color;

2:

importjava.awt.Font;

3:

importjava.awt.Graphics;

4;

5:

publicclassRollingMessageextendsjava.applet.Applet{

6:

7:

Strings="WelcometoHere!

";

8:

ints_length=s.length();//字符串长度

9:

intx_character=0;//显示到第几个字符

10:

FontwordFont=newFont("TimesRoman",Font.BOLD,50);

11:

12:

publicvoidstart(){

13:

while(true){

14:

if(x_character++>s_length)

15:

x_character=1;

16:

repaint();

17:

try{

18:

Thread.sleep(300);//暂停300毫秒

19:

}catch(InterruptedExceptione){}

20:

}

21:

}

22:

23:

publicvoidpaint(Graphicsg){

24:

g.setFont(wordFont);

25:

g.setColor(Color.red);

26;g.drawString(s.substring(0,x_character),8,50);

27:

}

28:

}

上述程序中的第18行调用了sleep()方法,它是Thread类中定义的一个类方法(即含

有static关键字的方法),调用它能使正在运行着的程序暂停指定的毫秒数。

如果不调用

sleep()方法,appplet就会全速运行,必将导致动画的换帧速度太快,用户就来不及看

清动画的内容,得到的只有乱闪的画面。

因而,动画的制作过程中需要不断地调整每帧

之间的时延数值,使其达到满意的播放速度。

程序中的第17行和第19行可能看起来有点

古怪,其实try和catch是为了让我们能完善的处理Java程序运行时产生的错误,也就是

异常处理(详见第7章)。

此时我们只需简单地认为,如果当程序正在执行try中的语句

时发生了异常情况,就由catch中的语句来处理。

另外,程序中的第26行用到了String类

中的提取子串的substring()方法,它的第一个参数是子串的起始字符(包括该字符),

第二个参数表示终止字符(不包括该字符)。

因而在paint()方法中每次都根据不同的

x_character值,显示不同长度的字符串。

如果真的上机去运行上面这段程序的话,你将会感到十分失望,因为屏幕上一片空白

,什么也没有。

问题出在哪里呢?

原来,我们的程序中调用repaint()方法时,系统只是

得到一个重画的请求,并不是立即去完成重画动作,而系统只能保证当它有空时,才能

真正去执行repaint()方法中的代码,即调用update()和paint()方法进行真正的重画

工作。

而目前的情况是在start()方法中用一个while无穷循环独占了系统资源,系统就

没有机会去完成重画工作。

更为严重的是,该applet还不能正常结束,因为系统同样也没

有机会去调用stop()方法。

那到底应该怎么办呢?

看了下一小节,自然就会得到答案。

4.3.2引入线程机制

以前的应用程序一般只有一条控制链,从程序初始化开始,然后调用其它方法,进行

数据处理,最后输出结果信息,直至退出操作系统,我们说这种应用程序就是单线程

(singlethread)的。

而Java中的多线程(multithread)则允许在一个程序里的同一时

刻中,并发运行各自的线程代码,并且各线程间还可以做到不相互影响。

因此,如果程序

中发现有可以同时运行的操作时,就可以启动一个新的线程去完成。

当然,由于机器的硬件资源(如CPU)是固定的,并不会因为程序中采用了多线程而

使运行速度加快,但是给用户的整体感觉会好些。

例如,某些集成开发环境软件,在进

行编译过程的同时,还可响应用户的其它操作请求,如继续编辑等,而不必等待漫长的

编译过程完全结束再进行下一步操作。

如果程序的执行瓶颈不在CPU的话,采用多线程还

可以提高程序的执行效率,例如通过网络获取多个数据文件,利用多线程比用单线程的

顺序载入要快得多(假设瓶颈也不在网络带宽)。

关于多线程的内容在第7章中还会详细

介绍。

上一小节的例子不能运行的原因就在于整个applet只用了一个线程,所以一旦start()

方法进入死循环后,整个线程就卡在那里。

因此,我们可以考虑再产生一个新的线程,

由它来专门执行while循环,定时发出重画请求,而系统就让原来的线程进行paint()操

作,这样两不相误,动画效果自然也就产生了。

说实话,在Java中编写多线程applet是非常容易的,下面我们就来一步步的修改刚才

的错误程序:

1.实现Runnable接口

在Java中有两种方法可将一个程序变成多线程的程序。

第一种是继承Thread类;

第二种就是实现Runnable接口。

由于Java不支持多重继承,而我们的applet已经继承了java.awt.Applet类,所以就不能再去继承Thread类。

这时,只有去实现Runnable接口来实现多线程,因此需将该applet的说明改为:

publicclassRollingMessageextendsjava.applet.AppletimplementsRunnable{

...

}

2.声明一个Thread类型的实例变量

该实例变量设为Thread类型,用来存放新的线程对象:

ThreadrunThread;

由于Thread类在java.lang程序包中,因而可以不用在程序头部指明import这个类。

3.覆盖start()方法

在start()方法中只需做一件事,那就是生成一个新线程并启动这个线程:

publicvoidstart(){

if(runThread==null){

runThread=newThread(this);

runThread.start();

}

}

这里用到了Thread类的构造方法,它的调用格式为:

Thread(Runnabletarget)

由于实现Runnable接口的正是RollingMessage类,因此target的参数值就设为this,

即本对象。

生成浏览一个Thread对象后,只要调用Thread类中的start()方法,就启动

了该线程。

具体的说,Thread类中的start()方法实际上是去调用Runnable接口中定义

的run()方法,从而完成了启动新线程的任务,同时立刻又将程序的控制权交回原来的

线程。

因而,可以这样说,当runThread.start();这个语句执行完后,该applet就有了两

个线程,一个运行原来applet中本身的代码,另一个运行下面就要讲到的run()方法中

的代码。

4.实现run()方法

既然我们把start()方法中的代码改为只生成并启动一个新的线程,那么我们原来

的applet中start()方法里面的代码都放到哪里去了呢?

那就是放在一个新的方法run()

中,事实上这也是Runnable接口中唯一定义的方法。

而run()方法中的代码可以是applet

中任何想分配给另一线程所做的工作。

在本例中,就是把原来start()方法中的主循环

代码,全部放入了run()方法里,因此也可以说run()方法中的代码才是这个applet真

正的核心:

publicvoidrun(){

while(true){

if(x_character++>s_length)

x_character=0;

repaint();

try{

Thread.sleep(300);

}catch(InterruptedExceptione){}

}

}

5.覆盖stop()方法

既然在applet的start()方法中生成并启动了一个新的线程,相应地,我们也应该在

applet被挂起时,停止这一线程的运行:

publicvoidstop(){

if(runThread!

=null){

runThread.stop();

runThread=null;

}

}

这里调用了Thread对象的stop()方法,就停止了该线程的运行,紧接着下一行就将

这个Thread对象设为null,这是为了让系统把这个无用的Thread当作垃圾收集掉,释放内存。

一旦用户重新回到该Web页面,applet又会在start()方法中重新产生新的线程并

启动它。

下面就是这一例子改正后的正确代码,其显示效果如图4-15所示。

importjava.awt.Color;

importjava.awt.Font;

importjava.awt.Graphics;

publicclassRollingMessageextendsjava.applet.AppletimplementsRunnable{

ThreadrunThread;

Strings="Welcometohere!

";

ints_length=s.length();

intx_character=0;

FontwordFont=newFont("TimesRoman",Font.BOLD,50);

publicvoidstart(){

if(runThread==null){

runThread=newThread(this);

runThread.start();

}

}

publicvoidstop(){

if(runThread!

=null){

runThread.stop();

runThread=null;

}

}

publicvoidrun(){

while(true){

if(x_character++>s_length)

x_character=0;

repaint();

try{

Thread.sleep(300);

}catch(InterruptedExceptione){}

}

}

publicvoidpaint(Graphicsg){

g.setFont(wordFont);

g.setColor(Color.red);

g.drawString(s.substring(0,x_character),8,50);

}

}

图4-15简单动画例子的执行结果

4.3.3初识闪烁问题

虽然,我们刚才的applet已经能动起来,可是挑剔的用户马上就会发现屏幕上的动

画会一闪一闪的,如果你的机器速度较慢的话,这一现象就更明显。

确实,有效的解决

闪烁问题一直是动画制作的关键技术,因为没有人会愿意观看刺眼的一闪一闪的画面。

既然想要解决这一问题,那就先要找到产生闪烁现象的原因在哪里。

还记不记得动

画过程是如何工作的:

applet调用repaint()方法通知系统进行重画,repaint()方法

实际调用了update()方法先清洗整个applet区域,然后再调用paint()方法绘制屏幕。

很明显,问题就出在update()方法里,它每次清除屏幕,使画面每次都从有内容,到

全空白,再画上内容,无疑造成了闪烁。

下面就是update()方法的缺省代码:

publicvoidupdate(Graphicsg){

g.setColor(getBackground());//将背景色置为当前绘图颜色

g.fillRect(0,0,width,height);//用背景色填充整个applet区域

g.setColor(getForeground());//将当前绘图颜色设回前景色

paint(g);//进行重画

}

其中width和height是指整个applet的高度和宽度。

很显然,我们必须覆盖这一方法,去改变它每次都呆板的用背景色去填充一下整个

applet区域。

那我们是否真的每次都需清除屏幕吗?

回想一下,我们的applet每一次重

画的内容都比前一次多一个字符。

所以,如果前一个画面不清除,后一个画面叠加上去

并不会破坏整个画面的内容,只有在全部字符都显示完全,才需要真正的清洗一下屏幕

因此,只在真正需要清除屏幕的时候才去做清除的动作,这应该说是消除闪烁的一个

原则。

下面,就是我们覆盖缺省的update()方法后的新代码:

publicvoidupdate(Graphicsg){

if(x_character==0){

g.setColor(getBackground());

g.fillRect(0,0,appletWidth,appletHeight);

g.setColor(getForeground());

}

paint(g);

}

另外有一点要改进的就是,为了能增加applet的灵活性和实用性,我们要尽量从HTML文件给applet传递各种配置参数,如要显示的字符串、字体、颜色、尺寸等信息,这样就不必每次小的改动都需要重新编译applet源代码。

下面就是本实例经改进后的程序代码:

importjava.awt.Color;

importjava.awt.Font;

importjava.awt.FontMetrics;

importjava.awt.Graphics;

publicclassRollingMessageextendsjava.applet.AppletimplementsRunnable{

ThreadrunThread;

Strings;//要显示的字符串

ints_length;//字符串的长度

intx_character=0,//当前显示到第几个字符串

y_coord,//字符串的Y坐标位置

textcolor,//字符串的颜色值(16进制整数rrggbb)

backcolor,//applet背景颜色值(16进制整数rrggbb)

delay;//每帧画面的时延(毫秒)

intappletWidth,appletHeight;

Stringfont_name;//字体的名称

intfont_size;//字体的尺寸

FontwordFont;

FontMetricswordMetrics;

publicvoidinit(){

Stringtemp;

appletWidth=size().width;

appletHeight=size().height;

temp=getParameter("font");

font_name=(temp==null)?

"TimesRoman":

temp;

temp=getParameter("fontsize");

font_size=(temp==null)?

12:

Integer.parseInt(temp);//转换为10进制整数

wordFont=newFont(font_name,Font.PLAIN,font_size);

if(wordFont==null)

wordFont=getFont();

wordMetrics=getFontMetrics(wordFont);

temp=getParameter("text");

s=(temp==null)?

"Messagegoeshere...":

temp;

s_length=s.length();

temp=getParameter("textcolor");

textcolor=(temp==null)?

0:

Integer.parseInt(temp,16);//转换为16进制整数

temp=getParameter("backcolor");

backcolor=(temp==null)?

0xffffff:

Integer.parseInt(temp,16);

temp=getParameter("delay");

delay=(temp==null)?

100:

Integer.parseInt(temp);

y_coord=appletHeight/2+(wordMetrics.getHeight()-wordMetrics.getDescent())/2;

}

publicvoidstart(){

if(runThread==null){

runThread=newThread(this);

runThread.start();

}

}

publicvoidstop(){

if(runThread!

=null){

runThread.stop();

runThread=null;

}

}

publicvoidrun(){

while(true){

if(x_character++>s_length)

x_character=0;

repaint();

try{

Thread.sleep(delay);

}catch(InterruptedExceptione){}

}

}

publicvoidpaint(Graphicsg){

g.setFont(wordFont);

g.setColor(Color.red);

g.drawString(s.substring(0,x_character),8,y_coord);

}

publicvoidupdate(Graphicsg){

if(x_character==0){

g.setColor(getBackground());

g.fillRect(0,0,appletWidth,appletHeight);

g.setColor(getForeground());

}

paint(g);

}

}

下面是测试这一applet的HTML语言的例子:

">

YouneedJavatousetheRollingMessageapplet!

4.3.4放映图像

有些人可能会觉得真正的动画应该是每一帧都有自己的图像,而不是仅仅跳出几个字

符。

那好吧,我们下面就来制作一个放映图像的动画例子,那就是一个会走的数码钟,

其显示效果如图4-16所示。

图4-16会走的数码钟

在放映图像前我们自然应该先准备好各个图像文件,本例的applet所需要的图像清单

是:

'0'到'9'共十幅数码管图像(文件名为lcd0.gif、lcd1.gif、...、lcd9.gif),一

个冒号图像(colon.gif文件),一个钟的边框(frame.gif文件)。

这些清单如图4-17

所示。

图4-17数码钟所用的

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 环境科学食品科学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1