承香墨影
Android--逐幀動畫FrameAnimation
前言
開門見山,本篇博客講解一下如何在Android平台下播放一個逐幀動畫。逐幀動畫在Android下可以通過代碼和XML文件兩種方式定義,本篇博客都將講到,最後將以一個簡單的Demo來演示兩種方式定義的逐幀動畫的播放。
本篇博客的主要內容:
Android中的逐幀動畫
使用XML定義的資源文件設置動畫幀
使用Java代碼創建逐幀動畫
Android中的逐幀動畫
先來說說什麼是逐幀動畫,逐幀動畫是一種常見的動畫形式(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循環播放
使用XML定義的資源文件設置動畫幀
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幀動畫的資源文件:
View Code
實現代碼:
復制代碼
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 }
復制代碼
使用Java代碼創建逐幀動畫
在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的運行效果: