前言
開門見山,本篇博客講解一下如何在Android平台下播放一個逐幀動畫。逐幀動畫在Android下可以通過代碼和XML文件兩種方式定義,本篇博客都將講到,最后將以一個簡單的Demo來演示兩種方式定義的逐幀動畫的播放。
本篇博客的主要內容:
先來說說什么是逐幀動畫,逐幀動畫是一種常見的動畫形式(Frame By Frame),其原理是在“連續的關鍵幀”中分解動畫動作,也就是在時間軸的每幀上逐幀繪制不同的內容,使其連續播放而成動畫。 因為逐幀動畫的幀序列內容不一樣,不但給制作增加了負擔而且最終輸出的文件量也很大,但它的優勢也很明顯:逐幀動畫具有非常大的靈活性,幾乎可以表現任何想表現的內容,而它類似與電影的播放模式,很適合於表演細膩的動畫。
在Android中逐幀動畫需要得到AnimationDrawable類的支持,它位於"android.graphics.drawable.AnimationDrawable"包下,是Drawable的間接子類。它主要用來創建一個逐幀動畫,並且可以對幀進行拉伸,把它設置為View的背景即可使用AnimationDrawable.start()方法播放。既然逐幀動畫是需要播放一幀一幀的圖像,所以需要為其添加幀。在Android中提供了兩種方式為AnimationDrawable添加幀:XML定義的資源文件和Java代碼創建,后面再詳細講講這兩種添加幀的方式。
光為AnimationDrawable設置幀還不能完成播放動畫的功能,還需要AnimationDrawable定義好的其他的一些方法來操作逐幀動畫,下面簡單介紹一下AnimationDrawable的常用方法:
- void start():開始播放逐幀動畫。
- void stop():停止播放逐幀動畫。
- void addFrame(Drawable frame,int duration):為AnimationDrawable添加一幀,並設置持續時間。
- int getDuration(int i):得到指定index的幀的持續時間。
- Drawable getFrame(int index):得到指定index的幀Drawable。
- int getNumberOfFrames():得到當前AnimationDrawable的所有幀數量。
- boolean isOneShot():當前AnimationDrawable是否執行一次,返回true執行一次,false循環播放。
- boolean isRunning():當前AnimationDrawable是否正在播放。
- void setOneShot(boolean oneShot):設置AnimationDrawable是否執行一次,true執行一次,false循環播放
Android下所有的資源文件均要放在/res目錄下,對於動畫幀的資源需要當成一個Drawable,所以需要把它放在/res/Drawable目錄下。而定義逐幀動畫非常簡單,只要在<animation-list.../>元素中使用<item.../>子元素定義動畫的全部幀,並制定各幀的持續時間即可。還可以在<animation-list.../>元素中添加屬性,來設定逐幀動畫的屬性。
例如:
1 <?xml version="1.0" encoding="utf-8"?> 2 <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false" > 3 <!-- 定義一個動畫幀,Drawable為img0,持續時間50毫秒 --> 4 <item android:drawable="@drawable/img0" android:duration="50" /> 5 </animation-list>
定義好逐幀動畫的資源文件之后,只需要使用getResources().getDrawable(int)方法獲取AnimationDrawable示例,然后把它設置為某個View的背景即可。
下面通過一個簡單的Demo,來演示如何播放一個XML定義的逐幀動畫,布局很簡單,一個ImageView來承載逐幀動畫,兩個Button控制播放與停止:
XML幀動畫的資源文件:

1 <?xml version="1.0" encoding="utf-8"?> 2 <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false" > 3 <!-- 定義一個動畫幀,Drawable為img0,持續時間50毫秒 --> 4 <item android:drawable="@drawable/img0" android:duration="50" /> 5 <item android:drawable="@drawable/img1" android:duration="50" /> 6 <item android:drawable="@drawable/img2" android:duration="50" /> 7 <item android:drawable="@drawable/img3" android:duration="50" /> 8 <item android:drawable="@drawable/img4" android:duration="50" /> 9 <item android:drawable="@drawable/img5" android:duration="50" /> 10 <item android:drawable="@drawable/img6" android:duration="50" /> 11 <item android:drawable="@drawable/img7" android:duration="50" /> 12 <item android:drawable="@drawable/img8" android:duration="50" /> 13 <item android:drawable="@drawable/img9" android:duration="50" /> 14 <item android:drawable="@drawable/img10" android:duration="50" /> 15 <item android:drawable="@drawable/img11" android:duration="50" /> 16 <item android:drawable="@drawable/img12" android:duration="50" /> 17 <item android:drawable="@drawable/img13" android:duration="50" /> 18 <item android:drawable="@drawable/img14" android:duration="50" /> 19 <item android:drawable="@drawable/img15" android:duration="50" /> 20 <item android:drawable="@drawable/img16" android:duration="50" /> 21 <item android:drawable="@drawable/img17" android:duration="50" /> 22 <item android:drawable="@drawable/img18" android:duration="50" /> 23 <item android:drawable="@drawable/img19" android:duration="50" /> 24 <item android:drawable="@drawable/img20" android:duration="50" /> 25 <item android:drawable="@drawable/img21" android:duration="50" /> 26 <item android:drawable="@drawable/img22" android:duration="50" /> 27 <item android:drawable="@drawable/img23" android:duration="50" /> 28 <item android:drawable="@drawable/img24" android:duration="50" /> 29 </animation-list>
實現代碼:
1 package cn.bgxt.frameanimationdemo; 2 3 import android.app.Activity; 4 import android.graphics.drawable.AnimationDrawable; 5 import android.os.Bundle; 6 import android.util.Log; 7 import android.view.View; 8 import android.view.View.OnClickListener; 9 import android.widget.Button; 10 import android.widget.ImageView; 11 import android.widget.Toast; 12 13 public class ToXMLActivity extends Activity { 14 15 private Button btn_start, btn_stop; 16 private ImageView iv_frame; 17 private AnimationDrawable frameAnim; 18 19 @Override 20 protected void onCreate(Bundle savedInstanceState) { 21 super.onCreate(savedInstanceState); 22 setContentView(R.layout.activity_frameanim); 23 24 btn_start = (Button) findViewById(R.id.btn_start); 25 btn_stop = (Button) findViewById(R.id.btn_stop); 26 27 btn_start.setOnClickListener(click); 28 btn_stop.setOnClickListener(click); 29 30 iv_frame = (ImageView) findViewById(R.id.iv_frame); 31 32 // 通過逐幀動畫的資源文件獲得AnimationDrawable示例 33 frameAnim=(AnimationDrawable) getResources().getDrawable(R.drawable.bullet_anim); 34 // 把AnimationDrawable設置為ImageView的背景 35 iv_frame.setBackgroundDrawable(frameAnim); 36 } 37 38 private View.OnClickListener click = new OnClickListener() { 39 40 @Override 41 public void onClick(View v) { 42 switch (v.getId()) { 43 case R.id.btn_start: 44 start(); 45 break; 46 case R.id.btn_stop: 47 stop(); 48 break; 49 default: 50 break; 51 } 52 } 53 }; 54 55 /** 56 * 開始播放 57 */ 58 protected void start() { 59 if (frameAnim != null && !frameAnim.isRunning()) { 60 frameAnim.start(); 61 Toast.makeText(ToXMLActivity.this, "開始播放", 0).show(); 62 Log.i("main", "index 為5的幀持續時間為:"+frameAnim.getDuration(5)+"毫秒"); 63 Log.i("main", "當前AnimationDrawable一共有"+frameAnim.getNumberOfFrames()+"幀"); 64 } 65 } 66 67 /** 68 * 停止播放 69 */ 70 protected void stop() { 71 if (frameAnim != null && frameAnim.isRunning()) { 72 frameAnim.stop(); 73 Toast.makeText(ToXMLActivity.this, "停止播放", 0).show(); 74 } 75 } 76 }
在Android中,除了可以通過XML文件定義一個逐幀動畫之外,還可以通過AnimationDrawable.addFrame()方法為AnimationDrawable添加動畫幀,上面已經提供了addFrame()的方法簽名,它可以設置添加動畫幀的Drawable和持續時間。其實沒什么技術含量,下面通過一個簡單的Demo演示一下。
實現代碼:
1 package cn.bgxt.frameanimationdemo; 2 3 import android.app.Activity; 4 import android.graphics.BitmapFactory; 5 import android.graphics.Canvas; 6 import android.graphics.ColorFilter; 7 import android.graphics.drawable.AnimationDrawable; 8 import android.graphics.drawable.BitmapDrawable; 9 import android.graphics.drawable.Drawable; 10 import android.os.Bundle; 11 import android.view.View; 12 import android.view.View.OnClickListener; 13 import android.widget.Button; 14 import android.widget.ImageView; 15 import android.widget.Toast; 16 17 public class ToCodeActivity extends Activity { 18 private Button btn_start, btn_stop; 19 private ImageView iv_frame; 20 private AnimationDrawable frameAnim; 21 22 @Override 23 protected void onCreate(Bundle savedInstanceState) { 24 super.onCreate(savedInstanceState); 25 setContentView(R.layout.activity_frameanim); 26 27 btn_start = (Button) findViewById(R.id.btn_start); 28 btn_stop = (Button) findViewById(R.id.btn_stop); 29 30 btn_start.setOnClickListener(click); 31 btn_stop.setOnClickListener(click); 32 33 iv_frame = (ImageView) findViewById(R.id.iv_frame); 34 35 frameAnim =new AnimationDrawable(); 36 // 為AnimationDrawable添加動畫幀 37 frameAnim.addFrame(getResources().getDrawable(R.drawable.img0), 50); 38 frameAnim.addFrame(getResources().getDrawable(R.drawable.img1), 50); 39 frameAnim.addFrame(getResources().getDrawable(R.drawable.img2), 50); 40 frameAnim.addFrame(getResources().getDrawable(R.drawable.img3), 50); 41 frameAnim.addFrame(getResources().getDrawable(R.drawable.img4), 50); 42 frameAnim.addFrame(getResources().getDrawable(R.drawable.img5), 50); 43 frameAnim.addFrame(getResources().getDrawable(R.drawable.img6), 50); 44 frameAnim.addFrame(getResources().getDrawable(R.drawable.img7), 50); 45 frameAnim.addFrame(getResources().getDrawable(R.drawable.img8), 50); 46 frameAnim.addFrame(getResources().getDrawable(R.drawable.img9), 50); 47 frameAnim.addFrame(getResources().getDrawable(R.drawable.img10), 50); 48 frameAnim.addFrame(getResources().getDrawable(R.drawable.img11), 50); 49 frameAnim.addFrame(getResources().getDrawable(R.drawable.img12), 50); 50 frameAnim.addFrame(getResources().getDrawable(R.drawable.img13), 50); 51 frameAnim.addFrame(getResources().getDrawable(R.drawable.img14), 50); 52 frameAnim.addFrame(getResources().getDrawable(R.drawable.img15), 50); 53 frameAnim.addFrame(getResources().getDrawable(R.drawable.img16), 50); 54 frameAnim.addFrame(getResources().getDrawable(R.drawable.img17), 50); 55 frameAnim.addFrame(getResources().getDrawable(R.drawable.img18), 50); 56 frameAnim.addFrame(getResources().getDrawable(R.drawable.img19), 50); 57 frameAnim.addFrame(getResources().getDrawable(R.drawable.img20), 50); 58 frameAnim.addFrame(getResources().getDrawable(R.drawable.img21), 50); 59 frameAnim.addFrame(getResources().getDrawable(R.drawable.img22), 50); 60 frameAnim.addFrame(getResources().getDrawable(R.drawable.img23), 50); 61 frameAnim.addFrame(getResources().getDrawable(R.drawable.img24), 50); 62 frameAnim.setOneShot(false); 63 64 // 設置ImageView的背景為AnimationDrawable 65 iv_frame.setBackgroundDrawable(frameAnim); 66 } 67 68 private View.OnClickListener click = new OnClickListener() { 69 70 @Override 71 public void onClick(View v) { 72 switch (v.getId()) { 73 case R.id.btn_start: 74 start(); 75 break; 76 case R.id.btn_stop: 77 stop(); 78 break; 79 default: 80 break; 81 } 82 83 } 84 }; 85 86 /** 87 * 開始播放 88 */ 89 protected void start() { 90 if (frameAnim != null && !frameAnim.isRunning()) { 91 frameAnim.start(); 92 Toast.makeText(ToCodeActivity.this, "開始播放", 0).show(); 93 } 94 } 95 /** 96 * 停止播放 97 */ 98 protected void stop() { 99 if (frameAnim != null && frameAnim.isRunning()) { 100 frameAnim.stop(); 101 Toast.makeText(ToCodeActivity.this, "停止播放", 0).show(); 102 } 103 } 104 105 }
其實上面兩個Demo實現的都是一種效果,是一個子彈擊中牆體的逐幀動畫效果,下面展示一下Demo的運行效果: