📄 jv0403.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0041)http://king.online.ha.cn/java/jc/Jyu2.htm -->
<HTML><HEAD><TITLE>第4章 Java对多媒体的支持</TITLE>
<META content="text/html; charset=gb2312" http-equiv=Content-Type>
<META content="MSHTML 5.00.2614.3500" name=GENERATOR>
<LINK href="java04.css" rel=stylesheet type=text/css>
<SCRIPT language=JavaScript>
<!-- Hide the script from old browsers --
// Michael P. Scholtis (mpscho@planetx.bloomu.edu)
// All rights reserved. January 15, 1996
// You may use this JavaScript example as you see fit, as long as the
// information within this comment above is included in your script.
function MakeArray(n){
this.length=n;
for(var i=1; i<=n; i++) this[i]=i-1;
return this
}
hex=new MakeArray(16);
hex[11]="A"; hex[12]="B"; hex[13]="C"; hex[14]="D"; hex[15]="E"; hex[16]="F";
function ToHex(x){ // Changes a int to hex (in the range 0 to 255)
var high=x/16;
var s=high+""; //1
s=s.substring(0,2); //2 the combination of these are the same as the trunc function
high=parseInt(s,10); //3
var left=hex[high+1]; // left part of the hex-value
var low=x-high*16; // calculate the rest of the values
s=low+""; //1
s=s.substring(0,2); //2 the combination of these are the same as the trunc function
low=parseInt(s,10); //3
var right=hex[low+1]; // right part of the hex-value
var string=left+""+right; // add the high and low together
return string;
}
function rainbow(text){
text=text.substring(3,text.length-4); // gets rid of the HTML-comment-tags
color_d1=255; // any value in 'begin' 0 to 255
mul=color_d1/text.length;
for(i=0;i<text.length;i++){
color_d1=255*Math.sin(i/(text.length/3)); // some other things you can try>> "=255-mul*i" to fade out, "=mul*i" to fade in, or try "255癕ath.sin(i/(text.length/3))"
color_h1=ToHex(color_d1);
color_d2=mul*i;
color_h2=ToHex(color_d2);
document.write("<FONT COLOR='#FF"+color_h1+color_h2+"'>"+text.substring(i,i+1)+'</FONT>');
}
}
// --End Hiding Here -->
</SCRIPT>
<SCRIPT LANUGAGE="JavaScript">
<!--
function pop(pageurl) {
var
popwin=window.open(pageurl,"popWin","scrollbars=yes,toolbar=no,location=no,directories=no,status=no,menubar=no,resizable=no,width=700,height=450");
return false;
}
//-->
</SCRIPT>
<STYLE type=text/css>.main {
FONT-FAMILY: "宋体"; FONT-SIZE: 9pt
}
</STYLE>
<SCRIPT language=LiveScript>
function WinOpen() {
msg=open("","DisplayWindow","toolbar=no,directories=no,menubar=no");
msg.document.write("");
msg.document.write("<CENTER><H1>酷毙了</H1><h2>这 是<B>JavaScript</B>所开的视窗!</h2></CENTER>");
}
</SCRIPT>
</HEAD>
<BODY background=bk.jpeg bgColor=#ffffff leftMargin=0 text=#000000
topMargin=0 marginwidth="0" marginheight="0" tracingopacity="35"
tracingsrc="file:///D|/tiu.jpg">
<p> </p><H3 align=center>第4章 Java对多媒体的支持</H3>
<p> <a name="43"></a>4.3 动画制作</P>
<p> 初步掌握了在Java中处理各种媒体的基本技能后,我们接下来要涉及的将是Java多媒体世界中最吸引人,最精彩的一部分--Java动画技术。这一节里,就让我们一起来由浅入深地制作几个动画实例,并通过这些实例引出一系列措施用以改进动画的显示效果,直至真正掌握Java动画技术的关键。</P>
<p> <a name="431"></a>4.3.1 一个简单实例</P>
<p> 其实,Java的动画原理也是很简单的,首先在屏幕上显示动画的第一帧(也就是第一幅画面),然后每隔很短的时间再显示另外一帧,如此往复。由于人眼的视觉暂停而感觉好象画面中的物体在运动。我们已经掌握了用paint(
)方法去显示静态帧的技能,接下来的问题就是如何不断地替换上其它帧画面。<br>
我们可以发现,当用paint( )方法在屏幕上画好一帧画面时,再用鼠标拖动这个窗口,或用其它窗口覆盖它再移开时,这帧画面并未被破坏,而是很快地就被重新画好了。原来,是系统发现屏幕上该区域的画面已遭破坏,就自动地再一次调用paint(
)方法将该画面恢复。说得更确切一些,系统其实是去调用repaint( )方法来完成重画任务,而repaint( )方法又去直接调用了update( )方法,update(
)方法则先清除整个applet区域里的内容,然后再调用paint( )方法,从而完成了一次重画工作。<br>
至此,我们似乎应该可以确定制作动画的基本方案了,那便是在applet开始运行后(即start( )方法中),每隔一段时间就取调用repaint( )方法来重画某一帧,而paint(
)方法中只需将相应的帧画到屏幕上。这一方案是否正确呢?我们就先来做一个简单实例试试看吧。<br>
这个实例很简单,是在applet中显示一行欢迎标题"Welcome to here!"。与以前不同的是,这行标题并不是一下子显示出来,而是象打字一般,一个个字母跳出来,然后全部隐去,再重复刚才的打字效果。用动画的术语来说,第一帧显示空白,第二帧显示"W",第三帧显示"We",直至最后一帧显示完整个字符串后,再循环到第一帧。根据上述提供的制作方案,我们很快就可以写出下面的程序:<br>
1: import java.awt.Color;<br>
2: import java.awt.Font;<br>
3: import java.awt.Graphics;<br>
4; <br>
5: public class RollingMessage extends java.applet.Applet{<br>
6: <br>
7: String s = "Welcome to Here !";<br>
8: int s_length = s.length(); //字符串长度<br>
9: int x_character = 0; //显示到第几个字符<br>
10: Font wordFont=new Font("TimesRoman" , Font.BOLD , 50);<br>
11: <br>
12: public void start() {<br>
13: while(true) {<br>
14: if (x_character++ > s_length)<br>
15: x_character = 1;<br>
16: repaint ();<br>
17: try {<br>
18: Thread.sleep(300); //暂停300毫秒<br>
19: } catch (InterruptedException e) {}<br>
20: }<br>
21: }<br>
22: <br>
23: public void paint (Graphics g) {<br>
24: g.setFont (wordFont);<br>
25: g.setColor (Color.red);<br>
26; g.drawString (s.substring(0,x_character), 8, 50);<br>
27: }<br>
28: }<br>
上述程序中的第18行调用了sleep( )方法,它是Thread类中定义的一个类方法(即含有static关键字的方法),调用它能使正在运行着的程序暂停指定的毫秒数。如果不调用sleep(
)方法,appplet就会全速运行,必将导致动画的换帧速度太快,用户就来不及看清动画的内容,得到的只有乱闪的画面。因而,动画的制作过程中需要不断地调整每帧之间的时延数值,使其达到满意的播放速度。程序中的第17行和第19行可能看起来有点古怪,其实try和catch是为了让我们能完善的处理Java程序运行时产生的错误,也就是异常处理(详见第7章)。此时我们只需简单地认为,如果当程序正在执行try中的语句时发生了异常情况,就由catch中的语句来处理。另外,程序中的第26行用到了String类中的提取子串的substring(
)方法,它的第一个参数是子串的起始字符(包括该字符),第二个参数表示终止字符(不包括该字符)。因而在paint( )方法中每次都根据不同的x_character值,显示不同长度的字符串。如果真的上机去运行上面这段程序的话,你将会感到十分失望,因为屏幕上一片空白,什么也没有。<br>
问题出在哪里呢?原来,我们的程序中调用repaint( )方法时,系统只是得到一个重画的请求,并不是立即去完成重画动作,而系统只能保证当它有空时,才能真正去执行repaint(
)方法中的代码,即调用update( )和paint( )方法进行真正的重画工作。而目前的情况是在start( )方法中用一个while无穷循环独占了系统资源,系统就没有机会去完成重画工作。更为严重的是,该applet还不能正常结束,因为系统同样也没有机会去调用stop(
)方法。那到底应该怎么办呢?看了下一小节,自然就会得到答案。</P>
<p> <a name="432"></a>4.3.2 引入线程机制</P>
<p> 以前的应用程序一般只有一条控制链,从程序初始化开始,然后调用其它方法,进行数据处理,最后输出结果信息,直至退出操作系统,我们说这种应用程序就是单线程(single
thread)的。而Java中的多线程(multithread)则允许在一个程序里的同一时刻中,并发运行各自的线程代码,并且各线程间还可以做到不相互影响。因此,如果程序中发现有可以同时运行的操作时,就可以启动一个新的线程去完成。<br>
当然,由于机器的硬件资源(如CPU)是固定的,并不会因为程序中采用了多线程而使运行速度加快,但是给用户的整体感觉会好些。例如,某些集成开发环境软件,在进行编译过程的同时,还可响应用户的其它操作请求,如继续编辑等,而不必等待漫长的编译过程完全结束再进行下一步操作。如果程序的执行瓶颈不在CPU的话,采用多线程还可以提高程序的执行效率,例如通过网络获取多个数据文件,利用多线程比用单线程的顺序载入要快得多(假设瓶颈也不在网络带宽)。关于多线程的内容在第7章中还会详细介绍。<br>
上一小节的例子不能运行的原因就在于整个applet只用了一个线程,所以一旦start()方法进入死循环后,整个线程就卡在那里。因此,我们可以考虑再产生一个新的线程,由它来专门执行while循环,定时发出重画请求,而系统就让原来的线程进行paint(
)操作,这样两不相误,动画效果自然也就产生了。<br>
说实话,在Java中编写多线程applet是非常容易的,下面我们就来一步步的修改刚才的错误程序:<br>
1. 实现Runnable接口<br>
在Java中有两种方法可将一个程序变成多线程的程序。第一种是继承Thread类;第二种就是实现Runnable接口。由于Java不支持多重继承,而我们的applet已经继承了java.awt.Applet类,所以就不能再去继承Thread类。这时,只有去实现Runnable接口来实现多线程,因此需将该applet的说明改为:<br>
public class RollingMessage extends java.applet.Applet implements Runnable {<br>
. . .<br>
}<br>
2. 声明一个Thread类型的实例变量<br>
该实例变量设为Thread类型,用来存放新的线程对象:<br>
Thread runThread;<br>
由于Thread类在java.lang程序包中,因而可以不用在程序头部指明import这个类。<br>
3. 覆盖start( )方法<br>
在start( )方法中只需做一件事,那就是生成一个新线程并启动这个线程:<br>
public void start() {<br>
if(runThread==null){<br>
runThread = new Thread(this);<br>
runThread.start();<br>
}<br>
}<br>
这里用到了Thread类的构造方法,它的调用格式为:<br>
Thread(Runnable target)<br>
由于实现Runnable接口的正是RollingMessage类,因此target的参数值就设为this,即本对象。生成浏览一个Thread对象后,只要调用Thread类中的start(
)方法,就启动了该线程。具体的说,Thread类中的start( )方法实际上是去调用Runnable接口中定义的run( )方法,从而完成了启动新线程的任务,同时立刻又将程序的控制权交回原来的线程。因而,可以这样说,当runThread.start();这个语句执行完后,该applet就有了两个线程,一个运行原来applet中本身的代码,另一个运行下面就要讲到的run(
)方法中的代码。<br>
4. 实现run( )方法<br>
既然我们把start( )方法中的代码改为只生成并启动一个新的线程,那么我们原来的applet中start( )方法里面的代码都放到哪里去了呢?那就是放在一个新的方法run()中,事实上这也是Runnable接口中唯一定义的方法。而run(
)方法中的代码可以是applet中任何想分配给另一线程所做的工作。在本例中,就是把原来start( )方法中的主循环代码,全部放入了run( )方法里,因此也可以说run(
)方法中的代码才是这个applet真正的核心:<br>
public void run() {<br>
while(true) {<br>
if (x_character++>s_length)<br>
x_character = 0;<br>
repaint ();<br>
try {<br>
Thread.sleep(300);<br>
} catch (InterruptedException e) {}<br>
}<br>
}<br>
5. 覆盖stop( )方法<br>
既然在applet的start( )方法中生成并启动了一个新的线程,相应地,我们也应该在applet被挂起时,停止这一线程的运行:<br>
public void stop() {<br>
if(runThread!=null){<br>
runThread.stop();<br>
runThread=null;<br>
}<br>
}<br>
这里调用了Thread对象的stop( )方法,就停止了该线程的运行,紧接着下一行就将这个Thread对象设为null,这是为了让系统把这个无用的Thread当作垃圾收集掉,释放内存。一旦用户重新回到该Web页面,applet又会在start(
)方法中重新产生新的线程并启动它。<br>
下面就是这一例子改正后的正确代码,其显示效果如图4-15所示。<br>
import java.awt.Color;<br>
import java.awt.Font;<br>
import java.awt.Graphics;<br>
public class RollingMessage extends java.applet.Applet implements Runnable {<br>
Thread runThread;<br>
String s = "Welcome to here !";<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -