如何制作動畫程序
■前言
在前一講中我們講述了如何利用低級API制作圖表應(yīng)用程序的方法。如果僅僅是圖表內(nèi)容的表示,那么在WEB上就可以用靜止的頁面表示。但是在手機(jī)應(yīng)用程序中表示的話,就能讓圖表內(nèi)容具有動感。這就是應(yīng)用程序與WEB的區(qū)別,這也正是其優(yōu)點(diǎn)所在。在本講中我們將進(jìn)一步講述如何在手機(jī)應(yīng)用程序中制作動畫,并詳細(xì)介紹其具體操作方法。
■4-1動畫
基本上,動畫能夠表現(xiàn)出文字、圖形、圖片等的描繪位置和畫面自身的連續(xù)變化。目前的程序中,按下按鍵,就會實(shí)行既定的方法。直到實(shí)行完既定方法之后才能進(jìn)行其他方法操作。但是,表示動畫過程中,不實(shí)行動畫表示以外的方法就沒有什么意義,因此應(yīng)該有必要在表示動畫的同時(shí)也可以實(shí)行其他的方法。所以掌握線程和計(jì)時(shí)器技術(shù)的知識和應(yīng)用是必不可少的。接下來,我們將詳細(xì)介紹線程和計(jì)時(shí)器。
■4-1-1線程
線程是執(zhí)行程序的最小單位,用來執(zhí)行Standalone的Java應(yīng)用程序的main()方法。當(dāng)你需要同步執(zhí)行多個(gè)操作,可以用多線程并發(fā)執(zhí)行。使用這種線程的程序被稱為“多線程程序”。
■4-1-1-1線程的使用
首先我們盡快學(xué)習(xí)怎樣使用“線程”。有兩種方法能使用線程:Runnable接口的使用方法和Thread類的繼承類的定義方法。在此,我們使用前一個(gè)方法—實(shí)現(xiàn)Runnable接口的方法。
實(shí)現(xiàn)Runnable接口的類必須要覆蓋run()方法。覆蓋的run()方法中記述了用線程執(zhí)行的處理。(ex.1)
classTestCanvasextendsCanvasimplementsRunnable{
/**
*線程執(zhí)行的處理
**/
publicvoidrun(){
//記述處理
.
.
}
/**
*描繪方法
**/
publicvoidpaint(Graphicsg){
//描繪處理
.
.
}
}
ex.1
另外一種方法是使用線程類Thread。如下所示:用構(gòu)造函數(shù)傳遞一個(gè)參數(shù)到該類的實(shí)例,該參數(shù)是實(shí)現(xiàn)Runnable接口的。使用start()方法就啟動該線程了。(ex.2)
Runnablerunnable=newTestCanvas();//實(shí)現(xiàn)Runnable類
Threadthread=newThread(runnable);
thread.start();
ex.2
■4-1-1-2制作簡單的動畫
接著我們來看線程如何被用于制作簡單的動畫。在下面的實(shí)例中,球從左向右運(yùn)動。(ex.3)
importjavax.microedition.lcdui.Display;
importjavax.microedition.midlet.MIDlet;
importjavax.microedition.midlet.MIDletStateChangeException;
/**
*球飛出去的實(shí)例動畫
*/
publicclassMovingBallextendsMIDlet{
privateDisplaydisplay;
privateMovingBallCanvascanvas;
/**
*構(gòu)造函數(shù)
*/
publicMovingBall(){
display=Display.getDisplay(this);
canvas=newMovingBallCanvas();
}
/**
*運(yùn)行時(shí)的處理
*/
protectedvoidstartApp()throwsMIDletStateChangeException{
display.setCurrent(canvas);
}
protectedvoidpauseApp(){}
protectedvoiddestroyApp(booleanarg0)throwsMIDletStateChangeException{}
}
importjavax.microedition.lcdui.Canvas;importjavax.microedition.lcdui.Graphics;
/**
*球飛出去的實(shí)例動畫的canvas。
*/
publicclassMovingBallCanvasextendsCanvasimplementsRunnable{
privateintx;//球的X坐標(biāo)
/**
*構(gòu)造函數(shù)
*/
publicMovingBallCanvas(){
Threadth=newThread(this);
th.start();
}
/**
*線程運(yùn)行的處理
*/
publicvoidrun(){
while(true){
x+=3;//更改球的坐標(biāo)
repaint();//再次描繪
try{
Thread.sleep(100);//100毫秒待機(jī)
}catch(InterruptedExceptione){
}
}
}
/**
*描繪方法
*/
protectedvoidpaint(Graphicsg){
//清除畫面
g.setColor(255,255,255);//白色
g.fillRect(0,0,getWidth(),getHeight());
//表示球
g.setColor(255,0,0);
g.fillArc(x,50,20,20,0,360);
}
}
Figure1
■4-1-1-3線程的待機(jī)處理
■4-1-1-3線程的待機(jī)處理
我們看看run()是如何支持等待機(jī)制的。如果沒有任何等待,球會迅速從左向右飛而我們無法察覺。為此,執(zhí)行Thread中的靜態(tài)方法sleep。在其參數(shù)中指定等待時(shí)間(單位毫秒)。(ex.4)
Figure1
■
4-1-1
-3線程的待機(jī)處理
我們看看run()是如何支持等待機(jī)制的。如果沒有任何等待,球會迅速從左向右飛而我們無法察覺。為此,執(zhí)行Thread中的靜態(tài)方法sleep。在其參數(shù)中指定等待時(shí)間(單位毫秒)。(ex.4)
Thread.sleep(100);//100毫秒待機(jī)
ex.4
如此例所示,處理和處理之間能夠待機(jī)100毫秒,按另外觀點(diǎn)看,與在100毫秒的定期間隔下運(yùn)行處理是一樣的。定期間隔下的處理,使用名為Timer的類同樣能夠?qū)崿F(xiàn)。下面說明一下Timer。
■4-1-2Timer
JAVA提供了計(jì)時(shí)器功能,它能有規(guī)則地、重復(fù)地執(zhí)行或者在指定時(shí)間里執(zhí)行。線程的運(yùn)算可以是連續(xù)的,而不僅僅是按規(guī)則的進(jìn)度。計(jì)時(shí)器的便利在于系統(tǒng)已經(jīng)為你準(zhǔn)備好特定的方法用于在指定時(shí)間或定時(shí)執(zhí)行。
■4-1-2-1時(shí)間的使用
Timer類和TimerTask類能夠?qū)崿F(xiàn)計(jì)時(shí)器的功能。
實(shí)例化一個(gè)TIMER的類,再調(diào)用一個(gè)繼承了TIMERTASK的類做SCHEDULE。這樣,TimerTask子類的run()就會在指定時(shí)間或周期被Timer實(shí)例呼叫。(表1)[!--empirenews.page--]
Table1
Timer
在Timer類中主要使用下面的方法。
•voidschedule(TimerTasktask,Datedate)
•voidschedule(TimerTasktask,Datedate,longperiod)
•voidschedule(TimerTasktask,longdelay)
•voidschedule(TimerTasktask,longdelay,longperiod)
•voidcancel()
這些schedule方法用于設(shè)定任務(wù)及何時(shí)執(zhí)行。若不設(shè)定第三個(gè)自變量period,在第二個(gè)自變量所指定的時(shí)間任務(wù)只執(zhí)行一次。否則第二個(gè)自變量所指定的時(shí)間之后,任務(wù)會反復(fù)執(zhí)行,自變量period是反復(fù)執(zhí)行時(shí)的延時(shí)。另外,呼叫出cancel方法,則會結(jié)束計(jì)時(shí)器程序。
TimerTask
TimerTask是抽象類。需要定義其子類。在所繼承的類中必須重載抽象方法run(),必須根據(jù)計(jì)時(shí)器的schedule定義具體的操作。
在TimerTask類中主要使用如下的方法。
•abstractvoidrun()
•voidcancel()
按照Timer類中所設(shè)定的schedule執(zhí)行run()方法。用cancel()方法能夠結(jié)束任務(wù)。
現(xiàn)在來看看用線程實(shí)現(xiàn)的圓球運(yùn)動的動畫--在這里我們也可以使用計(jì)時(shí)器來實(shí)現(xiàn)。如下所示:(ex.5)
importjavax.microedition.lcdui.Display;
importjavax.microedition.midlet.*;
/**
*球飛出去的動畫實(shí)例計(jì)時(shí)器版
*/
publicclassTimerMovingBallextendsMIDlet{
privateDisplaydisplay;
privateTimerMovingBallCanvascanvas;
/**
*構(gòu)造函數(shù)
*/
publicTimerMovingBall(){
display=Display.getDisplay(this);
canvas=newTimerMovingBallCanvas();
}
/**
*運(yùn)行時(shí)的處理
*/
protectedvoidstartApp()throwsMIDletStateChangeException{
display.setCurrent(canvas);
}
protectedvoidpauseApp(){}
protectedvoiddestroyApp(booleanarg0)throwsMIDletStateChangeException{}
}
importjava.util.*;
importjavax.microedition.lcdui.*;
/**
*球飛出去的實(shí)例動畫的canvas。
*/
publicclassTimerMovingBallCanvasextendsCanvas{
privateintx;//球的x坐標(biāo)
privateImageimg;
privateTimertimer;
privateTimerTasktask;
/**
*構(gòu)造函數(shù)
*/
publicTimerMovingBallCanvas(){
//讀取畫像
try{
img=Image.createImage("/back.PNG");
}catch(Exceptione){
e.printStackTrace();
}
//Timer,TimerTask的設(shè)定
timer=newTimer();
task=newTimerMovingBallTask(this);
timer.schedule(task,100,100);
//從第101毫秒開始在每100毫秒運(yùn)行任務(wù)
}
/**
*描繪方法
*/
protectedvoidpaint(Graphicsg){
g.drawImage(img,0,0,Graphics.LEFT|Graphics.TOP);
//表示球
g.setColor(255,0,0);
g.drawString(“withTimer”,0,0,Graphics.TOP|Graphics.LEFT);
g.fillArc(x,50,40,40,0,360);
}
/**
*能夠變化球的X坐標(biāo)
*/
publicvoidincrement(){
x+=3;
}
}
/**
*計(jì)時(shí)器任務(wù)根據(jù)計(jì)時(shí)器所設(shè)定的時(shí)間表,運(yùn)行run()方法。
*/
classTimerMovingBallTaskextendsTimerTask{
privateTimerMovingBallCanvascanvas;
/**
*構(gòu)造函數(shù)
*/
publicTimerMovingBallTask(TimerMovingBallCanvascanvas){
this.canvas=canvas;
}
/**
*呼叫計(jì)時(shí)器時(shí)所進(jìn)行的處理
*/
publicvoidrun(){
canvas.increment();
canvas.repaint();
}
}
ex.5
TimerMovingCanvas類的構(gòu)造函數(shù)中進(jìn)行下面的計(jì)時(shí)器設(shè)定。
//Timer,TimerTask的設(shè)定
timer=newTimer();
task=newTimerMovingBallTask(this);
timer.schedule(task,100,100);//從第101毫秒開始在每100毫秒運(yùn)行任務(wù)
在TimerMovingBallTask中,從TimerTask繼承的run()被調(diào)用,X坐標(biāo)的遞增使得球運(yùn)動起來。這樣有規(guī)律地使X坐標(biāo)遞增,就使得圓球在畫面上從左到右移動。(圖2)。
publicvoidrun(){
canvas.increment();
canvas.repaint();
}
図2
■雙緩沖
一旦進(jìn)行動畫就要頻繁地反復(fù)操作描繪處理,畫面處理中可能會突然發(fā)生屏幕閃爍。為了能夠控制屏幕的閃爍,一般會使用雙緩沖的手法。用雙緩沖存儲器并不是直接在畫面上進(jìn)行描繪,而是在被稱為屏幕外畫像的虛擬畫像上描繪全部之后,由于是表示畫面,所以能控制屏幕的閃爍。(圖3)
Figure3
根據(jù)手機(jī)機(jī)種的不同,事先將Canvas類與雙緩沖存儲器相對應(yīng)的情況也是有的。換言之,即使實(shí)現(xiàn)方?jīng)]有明確地定義雙緩沖存儲器,則在用雙緩沖存儲器的機(jī)制進(jìn)行描繪的情況下,沒必要特別關(guān)心閃爍的解決方案,但是要在不同的機(jī)型上運(yùn)行。N800的機(jī)型已經(jīng)能適應(yīng)此雙緩沖存儲器。為此,開發(fā)N800應(yīng)用程序的時(shí)候,有時(shí)會有必要在這闡述雙緩沖存儲器的處理。此時(shí),應(yīng)該知道終端是否適應(yīng)雙緩沖存儲器、以及是否有必要分開處理。[!--empirenews.page--]
使用Canvas類的isDoubleBufferd()方法就能知道是否適應(yīng)雙緩沖存儲器。由于使用這種方法可以區(qū)分開適應(yīng)雙緩沖存儲器的情況和不適應(yīng)適應(yīng)的情況,因此無論在什么樣的環(huán)境下都能夠有效控制閃爍現(xiàn)象。示例如下:(ex.6)
classDoubleBufferdCanvasextendsCanvas{
ImageoffImg=null;//關(guān)閉畫面的圖片
/**
*表示canvas之前所運(yùn)行的方法
*/
protectedvoidshowNotify(){
if(!isDoubleBuffered()){//關(guān)閉畫面的圖片
offImg=Image.createImage(getWidth(),getHeight());
}
}
/**
*描繪方法
*/
protectedvoidpaint(Graphicsg){
Graphicsbg=null;
if(offImg!=null){
bg=offImg.getGraphics();
}else{
bg=g;
}
//使用bg描繪
bg.setColor(255,255,255);
bg.fillRect(0,0,getWidth(),getHeight());
bg.setColor(255,0,0);
bg.drawArc(x,y,30,30,0,360);//描畫圓
//將offscreenimage導(dǎo)入畫面
if(!isDoubleBuffered()){
g.drawImage(offImg,0,0,Graphics.TOP|Graphics.LEFT);
bg=null;
}
}
}
ex.6
■制作應(yīng)用程序
4-3-1時(shí)鐘的應(yīng)用程序
那么,現(xiàn)在我們就作為示范使用動畫制作應(yīng)用程序吧。在本講中將要制作的應(yīng)用程序就是模擬表應(yīng)用程序。利用秒表,描繪出模擬表。在描繪模擬表針時(shí)使用三角函數(shù)。類結(jié)構(gòu)表如下:(表2)
Table2
4-3-2時(shí)間的設(shè)定
在模擬表應(yīng)用程序中秒針一秒動一下。因此,使用秒表,要設(shè)定每秒不同時(shí)刻的畫面更新。在ClockCanvas類的paint方法內(nèi)取得時(shí)刻,描繪鐘表。秒表任務(wù)如下所示:(ex.7)
/**********************************************
*秒表任務(wù)
**********************************************/
classClockTimerTaskextendsTimerTask{
privateClockCanvascanvas;
/**
*構(gòu)造函數(shù)
*/
publicClockTimerTask(ClockCanvascanvas){
this.canvas=canvas;
}
/**
*在每個(gè)指定時(shí)間內(nèi)運(yùn)行
*/
publicvoidrun(){
canvas.repaint();//再次描繪canvas
}
}
ex.7
定義完秒表任務(wù)后,就要在秒表上設(shè)定秒表任務(wù)。用ClockCanvas類的構(gòu)造函數(shù)進(jìn)行如下設(shè)定。(ex.8)
/**
*構(gòu)造函數(shù)
*/
publicClockCanvas(){
//設(shè)定秒表
timer=newTimer();
timerTask=newClockTimerTask(this);
timer.schedule(timerTask,1000,1000);//Onceevery1,000ms
}
ex.8
4-3-3描畫鐘表
設(shè)定完秒表后開始描繪鐘表。下面就是鐘表的框架(clock.png)。(圖4)
Figure4
接下來描畫秒針。因?yàn)橐鶕?jù)時(shí)刻變化秒針的位置,所以有必要根據(jù)時(shí)刻計(jì)算秒針的位置。在這兒用Graphics類的drawLine方法描繪秒針。在表的中心坐標(biāo)上固定住線的始點(diǎn),從時(shí)刻中計(jì)算出線的終點(diǎn)。
使用三角函數(shù)計(jì)算秒針終點(diǎn)的坐標(biāo)。表的中心坐標(biāo)是(x,y)、秒針的長度設(shè)為1,秒針的角度設(shè)為θ,那么終點(diǎn)的坐標(biāo)就是(x+l*cos(θ),y+l*sin(θ))。(圖5)
Figure5
三角函數(shù)的實(shí)際計(jì)算,在J2SE中,Math類有sin,cos方法,所以通常會使用這些方法,而J2ME中是沒有這種方法的。因此,在本講中定義了名為TrigonometricFunctions的類,將預(yù)先計(jì)算好的sin,cos值擴(kuò)大10000倍排列并保持在這個(gè)類中。所謂擴(kuò)大10000倍,是由于J2ME不支持double和float等小數(shù)點(diǎn)型,所以不能原封不動地保存小數(shù)點(diǎn)sin,cos。因此,用MIDP處理小數(shù)時(shí),將小數(shù)擴(kuò)大幾倍變成整數(shù)加以保持,實(shí)際上在使用時(shí),用事先乘出的數(shù)值再除以所乘的數(shù)值,計(jì)算并使用由此而得出的實(shí)際值。
然而,在J2ME下處理含有小數(shù)的數(shù)值時(shí),由于要將小數(shù)四舍五入成整數(shù)所以就會產(chǎn)生誤差。例如:0.12341234…四舍五入成整數(shù)就是0,這與原來的0.12341234…是有誤差的。由于要盡量減少誤差,所以計(jì)算之前要盡可能的乘以大數(shù)值并且有必要保存其整數(shù)。例如:在先前的0.12341234…基礎(chǔ)上乘以1000后就變成了123.41234…,小數(shù)點(diǎn)以下四舍五入后就是123。將123除以1000后就是0.123。這與開始的數(shù)值之間的誤差僅為0.00041234…,原封不動的將原來的數(shù)值四舍五入后數(shù)值0產(chǎn)生的誤差是0.00041234…,二者相比則前者的誤差要小的多。總之,小數(shù)上乘以的數(shù)值越大四舍五入后與原來的數(shù)值之間的誤差就越小。
先講sin,cos的數(shù)值擴(kuò)大10000倍,秒針坐標(biāo)計(jì)算的最后在除以10000。N800畫面尺寸的縱長為180,那么進(jìn)行180*x(只有X為小數(shù))計(jì)算時(shí)的最大誤差是
0.0000999...*180=0.017999...isapproximately0.0180
總是比1小,沒有四舍五入的誤差。
接下來描繪秒針。SecondAngle作為秒針的角度,SECOND_LENGTH是秒針的長度,表的中心坐標(biāo)是(CENTER_X,CENTER_Y)。(ex.9)
intsecondX=CENTER_X+TrigonometricFunctions.COS[secondAngle]*SECOND_LENGTH/10000;
intsecondY=CENTER_Y-TrigonometricFunctions.SIN[secondAngle]*SECOND_LENGTH/10000;
g.drawLine(CENTER_X,CENTER_Y,secondX,secondY);
ex.9
下面是ClockCanvas類的源碼。畫面上有表的中心坐標(biāo)。
importjava.util.*;
importjavax.microedition.lcdui.*;[!--empirenews.page--]
/**
*時(shí)鐘的畫面
*/
publicclassClockCanvasextendsCanvas{
privateClockTimerTasktimerTask;
privateTimertimer;
privatefinalStringIMAGE_PATH="/clock.png";
privatefinalintCLOCK_WIDTH=150;//時(shí)鐘的畫面寬度
privatefinalintCENTER_X=5+CLOCK_WIDTH/2;
privatefinalintCENTER_Y=CLOCK_WIDTH/2;
privatefinalintHOUR_LENGTH=CLOCK_WIDTH/4;//時(shí)鐘的長度
privatefinalintMINUTE_LENGTH=CLOCK_WIDTH*2/5;//分鐘的長度
privatefinalintSECOND_LENGTH=CLOCK_WIDTH*2/5;//秒鐘的長度
privateImageimg;//畫面的背景
/**
*構(gòu)造函數(shù)
*/
publicClockCanvas(){
//設(shè)置timer
timer=newTimer();
timerTask=newClockTimerTask(this);
timer.schedule(timerTask,1000,1000);
//登陸背景畫面
try{
img=Image.createImage(IMAGE_PATH);
}catch(Exceptione){
e.printStackTrace();
}
}
/**
*畫時(shí)鐘
*/
protectedvoidpaint(Graphicsg){
//背景的設(shè)置
g.setColor(255,255,255);
g.fillRect(0,0,getWidth(),getHeight());
g.drawImage(img,5,0,Graphics.TOP|Graphics.LEFT);
//獲得時(shí)間
Calendarcal=Calendar.getInstance();
inthour=(int)cal.get(Calendar.HOUR);
inthour_of_day=(int)cal.get(Calendar.HOUR_OF_DAY);
intminute=(int)cal.get(Calendar.MINUTE);
intsecond=(int)cal.get(Calendar.SECOND);
intyear=(int)cal.get(Calendar.YEAR);
intmonth=(int)cal.get(Calendar.MONTH)+1;
intday=(int)cal.get(Calendar.DAY_OF_MONTH);
//顯示時(shí)間
g.setColor(0,0,200);
StringdateString=year+"/"+month+"/"+day+""+hour_of_day+":"+minute+":"+second;
g.drawString(dateString,CENTER_X,getHeight()-25,Graphics.TOP|Graphics.HCENTER);
//時(shí)鐘時(shí)針的顯示
g.setColor(100,100,100);
inthourAngle=360-30*hour+90;
if(hourAngle>=360)hourAngle-=360;
inthourX=CENTER_X+TrigonometricFunctions.COS[hourAngle]*HOUR_LENGTH/TrigonometricFunctions.MULTIPLE;
inthourY=CENTER_Y-TrigonometricFunctions.SIN[hourAngle]*HOUR_LENGTH/TrigonometricFunctions.MULTIPLE;
//描繪時(shí)針
for(inti=-2;i<3;i++){
g.drawLine(CENTER_X+i,CENTER_Y+i,hourX,hourY);
g.drawLine(CENTER_X-i,CENTER_Y+i,hourX,hourY);
}
//分針的顯示
intminuteAngle=360-6*minute+90;
if(minuteAngle>=360)minuteAngle-=360;
intminuteX=CENTER_X+TrigonometricFunctions.COS[minuteAngle]*MINUTE_LENGTH/TrigonometricFunctions.MULTIPLE;
intminuteY=CENTER_Y-TrigonometricFunctions.SIN[minuteAngle]*MINUTE_LENGTH/TrigonometricFunctions.MULTIPLE;//j`
//畫分針的范圍
for(inti=-2;i<3;i++){
g.drawLine(CENTER_X+i,CENTER_Y+i,minuteX,minuteY);
g.drawLine(CENTER_X-i,CENTER_Y+i,minuteX,minuteY);
}
//秒針的顯示
intsecondAngle=360-6*second+90;
if(secondAngle>=360)secondAngle-=360;
intsecondX=CENTER_X+TrigonometricFunctions.COS[secondAngle]*SECOND_LENGTH/TrigonometricFunctions.MULTIPLE;
intsecondY=CENTER_Y-TrigonometricFunctions.SIN[secondAngle]*SECOND_LENGTH/TrigonometricFunctions.MULTIPLE;
g.drawLine(CENTER_X,CENTER_Y,secondX,secondY);
}
}
Figure6
4-3-4
擴(kuò)展函數(shù)
在本講中制作的模擬表應(yīng)用程序是實(shí)現(xiàn)了表的最小功能的示范應(yīng)用程序。在本應(yīng)用程序中,也可以自己增加鬧鐘和跑秒等擴(kuò)展功能,這些自己都嘗試一番哦。
4-4總結(jié)
在本講中我們講述了制作動畫應(yīng)用程序的具體方法。使用動畫,就要對畫像賦予動感,這比游戲還要復(fù)雜,所以應(yīng)該能夠制作圖表應(yīng)用程序。要運(yùn)行動畫,線據(jù)和秒表是不可缺少的基本技術(shù),但是線據(jù)知識是一門深奧的技術(shù)。參照本講中的樣品,加深線據(jù)方面的知識,就能夠制作成不錯(cuò)的應(yīng)用程序。在下一講中,我們將就效果音和BGM、聲音等技術(shù)進(jìn)行詳細(xì)講解。敬請關(guān)注!
4-3-4完成
以下是所制作的程序的源碼。
[!--empirenews.page--] 運(yùn)行結(jié)果如下所示:(図6). Figure6 4-3-4 擴(kuò)展函數(shù) 在本講中制作的模擬表應(yīng)用程序是實(shí)現(xiàn)了表的最小功能的示范應(yīng)用程序。在本應(yīng)用程序中,也可以自己增加鬧鐘和跑秒等擴(kuò)展功能,這些自己都嘗試一番哦。 4-4總結(jié) 在本講中我們講述了制作動畫應(yīng)用程序的具體方法。使用動畫,就要對畫像賦予動感,這比游戲還要復(fù)雜,所以應(yīng)該能夠制作圖表應(yīng)用程序。要運(yùn)行動畫,線據(jù)和秒表是不可缺少的基本技術(shù),但是線據(jù)知識是一門深奧的技術(shù)。參照本講中的樣品,加深線據(jù)方面的知識,就能夠制作成不錯(cuò)的應(yīng)用程序。在下一講中,我們將就效果音和BGM、聲音等技術(shù)進(jìn)行詳細(xì)講解。敬請關(guān)注!