1. Animation動畫
在Android 中,系統提供了動畫類 Animation ,其中又分為四種動畫效果:
● AlphaAnimation:透明度漸變動畫
● ScaleAnimation:漸變尺寸縮放動畫;
● TranslateAnimation:移動動畫
● RotateAnimation:旋轉動畫
這4種動畫效果的創建方法如下:
(1) AlphaAnimation 透明度漸變動畫
Animation alphaA = new AlphaAnimation(float fromAlpha,float toAlpha)
第一個參數:動畫開始時的透明度
第二個參數:動畫結束時的透明度
兩個參數的取值范圍為[0,1],從完全透明到完全不透明。
(2)ScaleAnimation 漸變尺寸縮放動畫
Animation scaleA = new ScaleAnimation(float fromX,float toX,float fromY,float toY,int pivotXType,float pivotXValue,int pivoteYType,float pivoteYValue)
第一個參數:動畫起始時X坐標上的伸縮比例
第二個參數:動畫結束時X坐標上的伸縮比例
第三個參數:動畫起始時Y坐標上的伸縮比例
第四個參數:動畫結束時Y坐標上的伸縮比例
第五個參數:動畫在X軸相對於物體的位置類型
第六個參數:動畫相對於物體X坐標的位置
第七個參數:動畫在Y軸相對於物體的位置類型
第八個參數:動畫相對於物體Y坐標的位置
其中位置類型分為以下三種:
Animation.ABSOLUTE:相對位置是屏幕左上角,絕對位置;
Animation.RELATIVE_TO_SELF:相對位置是自身 View, 取值為 0 時,表示相對於是自身左上角,取值為1是相對於自身的右下角;
Animation.RELATIVE_TO_PAREND:相對父類 View 的位置。
(3)TranslateAnimation 移動動畫
Animation translateA = new TranslateAnimation(float fromXDelta,float toXDelta,float fromYDelta,float YDelta)
第一個參數:動畫起始時X軸上的位置
第二個參數:動畫結束時X軸上的位置
第三個參數:動畫起始時Y軸上的位置
第四個參數:動畫結束時Y軸上的位置
(4)RotateAnimation 旋轉動畫
Animation rotateA = new RotateAnimation(float fromDegrees,float toDegrees,int pivotXType,float pivotXValue,int pivotYType,float pivotYValue)
第一個參數:動畫起始時旋轉角度
第二個參數:動畫旋轉到的角度
第三個參數:動畫在X軸相對於物件位置類型
第四個參數:動畫相對於物件的X坐標的開始位置
第五個參數:動畫在Y軸相對於物體的位置類型
第六個參數:動畫相對於物體的Y坐標的位置
不管哪一種動畫,都有一些通用方法,比如:
restart():重新播放動畫;
setDuration(int time):設置動畫播放時間,單位是毫秒。
下面是實例演示,效果如下:
新建項目,游戲框架為 View 游戲框架,
具體步驟參照“10.游戲開發基礎(View 游戲框架)”。
修改 MyView 類,代碼如下:
package com.example.ex4_10; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.KeyEvent; import android.view.View; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.RotateAnimation; import android.view.animation.ScaleAnimation; import android.view.animation.TranslateAnimation; public class MyView extends View implements AnimationListener{ private Paint paint; public MyView(Context context) { super(context); paint = new Paint(); paint.setColor(Color.WHITE); paint.setTextSize(20); setFocusable(true); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Bitmap bmp = BitmapFactory.decodeResource(this.getResources(), R.drawable.pic01); //黑色背景 canvas.drawColor(Color.BLACK); canvas.drawText("方向鍵 ↑ 漸變透明度動畫效果", 80, this.getHeight()-80, paint); canvas.drawText("方向鍵 ↓ 漸變尺寸伸縮動畫效果", 80, this.getHeight()-60, paint); canvas.drawText("方向鍵 → 畫面轉換位置移動動畫效果", 80, this.getHeight()-40, paint); canvas.drawText("方向鍵 ← 畫面轉移旋轉動畫效果", 80, this.getHeight()-20, paint); //繪制位圖,居中 canvas.drawBitmap(bmp, this.getWidth()/2-bmp.getWidth()/2, this.getHeight()/2-bmp.getHeight()/2, paint); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if(keyCode==KeyEvent.KEYCODE_DPAD_UP) { //漸變透明效果 Animation m1 = new AlphaAnimation(0.1f, 1.0f); //設置動畫播放時間為3秒 m1.setDuration(3000); //啟動動畫效果 this.startAnimation(m1); }else if(keyCode==KeyEvent.KEYCODE_DPAD_DOWN) { //漸變尺寸縮放動畫效果 Animation m2 = new ScaleAnimation(0.0f, 2.0f, 1.5f, 1.5f,Animation.RELATIVE_TO_PARENT,0.5f,Animation.RELATIVE_TO_PARENT,0.0f); m2.setDuration(2000); this.startAnimation(m2); }else if(keyCode==KeyEvent.KEYCODE_DPAD_LEFT) { //移動動畫效果 Animation m3 = new TranslateAnimation(0, 100, 0, 100); m3.setDuration(2000); this.startAnimation(m3); }else if(keyCode==KeyEvent.KEYCODE_DPAD_RIGHT) { //旋轉動畫效果,這里是旋轉360° Animation m4 = new RotateAnimation(0.0f, 360.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); m4.setDuration(2000); this.startAnimation(m4); } return super.onKeyDown(keyCode, event); } /** * 動畫開始時響應的函數 */ @Override public void onAnimationStart(Animation animation) { } /** * 動畫結束時響應的函數 */ @Override public void onAnimationEnd(Animation animation) { } /** * 動畫重播時響應的函數 */ @Override public void onAnimationRepeat(Animation animation) { } }
記住,Animation 的每種動畫都是對整個畫布進行操作。
2.自定義動畫
(1)動態位圖
在之前隨筆中,曾在屏幕上讓文本字符串跟隨玩家手指移動,從而行程一個動態的效果;那么讓一張位圖形成動態效果也很容易,只要不斷改變位圖的X或者Y軸的坐標即可。下面利用一張位圖形成海的波浪效果。
新建項目,游戲框架為SurfaceView 框架,准備圖片water.png如下:

修改MySurfaceView 類,代碼如下:
package com.example.ex4_11; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; public class MySurfaceView extends SurfaceView implements Callback, Runnable { // 用於控制SurfaceView 的大小、格式等,並且主要用於監聽SurfaceView 的狀態 private SurfaceHolder sfh; // 聲明一張波浪圖 private Bitmap bmp; // 聲明波浪圖的X,Y軸坐標 private int bmpX, bmpY; // 聲明一個畫布 private Canvas canvas; // 聲明一個線程 private Thread th; // 線程消亡的標識符 private boolean flag; public MySurfaceView(Context context) { super(context); // 實例SurfaceView sfh = this.getHolder(); // 為SurfaceView添加狀態監聽 sfh.addCallback(this); } /** * SurfaceView 視圖創建,響應此函數 */ @Override public void surfaceCreated(SurfaceHolder holder) { bmp = BitmapFactory.decodeResource(this.getResources(), R.drawable.water); // 讓位圖初始化X軸正好充滿屏幕 bmpX = -bmp.getWidth() + this.getWidth(); // 讓位圖繪制在畫布的最下方,且圖片Y坐標正好是(屏幕高-圖片高) bmpY = this.getHeight() - bmp.getHeight(); flag = true; // 實例線程 th = new Thread(this); // 啟動線程 th.start(); } /** * SurfaceView 視圖狀態發生改變時,響應此函數 */ @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } /** * SurfaceView 視圖消亡時,響應此函數 */ @Override public void surfaceDestroyed(SurfaceHolder holder) { flag = false; } private void myDraw() { try { canvas = sfh.lockCanvas(); if (canvas != null) { //刷屏,畫布白色 canvas.drawColor(Color.WHITE); //繪制位圖 canvas.drawBitmap(bmp, bmpX, bmpY, new Paint() ); } } catch (Exception e) { // TODO: handle exception } finally { if (canvas != null) { sfh.unlockCanvasAndPost(canvas); } } } /** * 游戲邏輯 */ private void logic() { bmpX += 5; } @Override public void run() { while (flag) { long start = System.currentTimeMillis(); myDraw(); logic(); long end = System.currentTimeMillis(); try { if (end - start < 50) { Thread.sleep(50 - (end - start)); } } catch (Exception e) { e.printStackTrace(); } } } }
(2)幀動畫
前面是利用改變位圖的X或者Y坐標行程動畫效果。當然在游戲開發中,很多動態的幀數不僅僅只有一幀,而所謂幀動畫,其實就是一幀一幀按照一定的順序進行播放實現的。下面是實例演示,效果如下:

新建項目,游戲框架為 SurfaceView 框架,在項目中導入下面6張png圖片:






修改MySurfaceView 類,代碼如下:
package com.example.ex4_11; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; public class MySurfaceView extends SurfaceView implements Callback, Runnable { // 用於控制SurfaceView 的大小、格式等,並且主要用於監聽SurfaceView 的狀態 private SurfaceHolder sfh; // 聲明一個畫布 private Canvas canvas; // 聲明一個線程 private Thread th; // 線程消亡的標識符 private boolean flag; private Paint paint; // 首先聲明6個容量的位圖數組 private Bitmap wifiBmp[] = new Bitmap[6]; // 對應6張圖片的Id private int[] wifiBmpId = new int[] { R.drawable.wifi01, R.drawable.wifi02, R.drawable.wifi03, R.drawable.wifi04, R.drawable.wifi05, R.drawable.wifi06 }; // 記錄當前播放幀 private int currentFrame; public MySurfaceView(Context context) { super(context); // 實例SurfaceView sfh = this.getHolder(); // 為SurfaceView添加狀態監聽 sfh.addCallback(this); paint = new Paint(); // 畫筆無鋸齒 paint.setAntiAlias(true); // 將幀圖放入幀數組 for (int i = 0; i < wifiBmp.length; i++) { wifiBmp[i] = BitmapFactory.decodeResource(this.getResources(), wifiBmpId[i]); } } /** * SurfaceView 視圖創建,響應此函數 */ @Override public void surfaceCreated(SurfaceHolder holder) { flag = true; // 實例線程 th = new Thread(this); // 啟動線程 th.start(); } /** * SurfaceView 視圖狀態發生改變時,響應此函數 */ @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } /** * SurfaceView 視圖消亡時,響應此函數 */ @Override public void surfaceDestroyed(SurfaceHolder holder) { flag = false; } private void myDraw() { try { canvas = sfh.lockCanvas(); if (canvas != null) { // 刷屏,畫布白色 canvas.drawColor(Color.WHITE); // 繪制位圖,居中顯示 canvas.drawBitmap( wifiBmp[currentFrame], this.getWidth() / 2 - wifiBmp[currentFrame].getWidth() / 2, this.getHeight() / 2 - wifiBmp[currentFrame].getHeight() / 2, paint); } } catch (Exception e) { // TODO: handle exception } finally { if (canvas != null) { sfh.unlockCanvasAndPost(canvas); } } } /** * 游戲邏輯 */ private void logic() { currentFrame++; // 當播放的當前幀大於並且等於幀數組時,重置當前幀為0 if (currentFrame >= wifiBmp.length) { currentFrame = 0; } } @Override public void run() { while (flag) { long start = System.currentTimeMillis(); myDraw(); logic(); long end = System.currentTimeMillis(); try { if (end - start < 500) { Thread.sleep(500 - (end - start)); } } catch (Exception e) { e.printStackTrace(); } } } }
(3)剪切圖動畫
剪切圖動畫類似於幀動畫的形式,唯一的區別就是動態幀全部放在了一張圖片中,然后通過設置可視區域完成。簡單的解釋,繪圖時通過控制X軸或者Y軸坐標,來逐幀顯示要顯示的圖片部分,達到動態的效果。下面是實例,運行效果和前面的幀動畫一樣。步驟如下:
新建項目,游戲框架為 SurfaceView 框架,在項目中導入下面這張png圖片:

修改MySurfaceView 類,代碼如下:
package com.example.ex4_11; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; public class MySurfaceView extends SurfaceView implements Callback, Runnable { // 用於控制SurfaceView 的大小、格式等,並且主要用於監聽SurfaceView 的狀態 private SurfaceHolder sfh; // 聲明一個畫布 private Canvas canvas; // 聲明一個線程 private Thread th; // 線程消亡的標識符 private boolean flag; private Paint paint; // 創建位圖 private Bitmap bmp = BitmapFactory.decodeResource(this.getResources(), R.drawable.wifi_all); // 記錄當前播放幀 private int currentFrame; public MySurfaceView(Context context) { super(context); // 實例SurfaceView sfh = this.getHolder(); // 為SurfaceView添加狀態監聽 sfh.addCallback(this); paint = new Paint(); // 畫筆無鋸齒 paint.setAntiAlias(true); } /** * SurfaceView 視圖創建,響應此函數 */ @Override public void surfaceCreated(SurfaceHolder holder) { flag = true; // 實例線程 th = new Thread(this); // 啟動線程 th.start(); } /** * SurfaceView 視圖狀態發生改變時,響應此函數 */ @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } /** * SurfaceView 視圖消亡時,響應此函數 */ @Override public void surfaceDestroyed(SurfaceHolder holder) { flag = false; } private int minW = bmp.getWidth() / 6; private void myDraw() { try { //居中點X軸坐標 int cW = this.getWidth() / 2 - minW / 2; //居中點Y軸坐標 int cH = this.getHeight() / 2 - bmp.getHeight() / 2; canvas = sfh.lockCanvas(); if (canvas != null) { // 刷屏,畫布白色 canvas.drawColor(Color.WHITE); canvas.save(); // 設置畫布的可視區域,大小為每幀的大小,居中 canvas.clipRect(cW, cH, cW + minW, cH + bmp.getHeight()); // 繪制位圖,居中 canvas.drawBitmap(bmp, cW - currentFrame * minW, cH, paint); canvas.restore(); } } catch (Exception e) { // TODO: handle exception } finally { if (canvas != null) { sfh.unlockCanvasAndPost(canvas); } } } /** * 游戲邏輯 */ private void logic() { currentFrame++; // 當播放的當前幀大於並且等於幀數組時,重置當前幀為0 if (currentFrame >= 6) { currentFrame = 0; } } @Override public void run() { while (flag) { long start = System.currentTimeMillis(); myDraw(); logic(); long end = System.currentTimeMillis(); try { if (end - start < 500) { Thread.sleep(500 - (end - start)); } } catch (Exception e) { e.printStackTrace(); } } } }
