事實上每一個View中都有Canvas能夠用來繪制動畫。僅僅須要在這個View中重載onDraw()方法就能夠,可是SurfaceView類是一個專門用來制動動畫的類。
Canvas(中文叫做"畫布")就和HTML5中的canvas標簽一樣能夠在一定區域內自由繪制圖形。Canvas+SurfaceView制作的動畫與View Animation和Property Animation這類動畫比起來更加適合大量的集中播放的動畫,比方游戲畫面、相機的圖像顯示等。
由於SurfaceView一般會在還有一個專門的線程中不斷重繪界面。所以不像其它動畫那樣要在主線程(UI線程)中播放動畫的同一時候還要消耗一定的流暢度用來響應用戶輸入。
在使用SurfaceView時須要注意以下這些要點:
1)每一個SurfaceView都須要一個SurfaceHolder對象來處理這個SurfaceView的生命周期和獲取這個SurfaceView的Canvas對象,能夠通過調用SurfaceView的getHolder()方法來獲取它的SurfaceHolder對象。
2)使用SurfaceView時通常是通過繼承SurfaceView的方式來實現,能夠順便implements兩個接口,各自是Runnable和SurfaceHolder.Callback。第二個接口須要重載三個函數,這三個函數就是SurfaceView的生命周期處理了,能夠通過SurfaceHolder對象的addCallback()方法把實現好的Callback對象傳進去。
3)在使用SurfaceView的Canvas時一定要記得加鎖同步。由於不能讓畫布同一時候繪制多個圖案,通過調用這個SurfaceView的SurfaceHolder對象的lockCanvas()就能夠做到這一點。繪制完成后在調用SurfaceHolder對象的unlockCanvasAndPost()方法就能夠解鎖並更新。
以下給出了一個用SurfaceView和Canvas繪制動畫的樣例,一般直接復制上就能夠執行看到效果:
package com.example.canvastest;
import android.app.Activity;
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.os.Bundle;
import android.view.Menu;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;
public class MainActivity extends Activity {
/*
* 這個類用來當測試的物件,會沿着方形路線持續移動
*/
class GameObject {
private float x;
private float y;
private Bitmap img;
private Paint paint;
public GameObject() {
this.img = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
this.x = 100;
this.y = 100;
this.paint = new Paint();
}
// 在SurfaceView加鎖同步后傳給自己的Canvas上繪制自己
public void drawSelf(Canvas canvas) {
canvas.drawBitmap(img, x, y, paint);
}
// 獲取物件下一次要繪制的位置(這里是沿着一個邊長為400的正方形不斷運動的)
public void getNextPos() {
if (y == 100 && x != 500)
x += 5;
else if (x == 500 && y != 500)
y += 5;
else if (y == 500 && x != 100)
x -= 5;
else if (x == 100 && y != 100)
y -= 5;
}
}
/*
* 這個類就是加工了SurfaceView之后的類,全部要運動的物件都終於放在這里進行繪制
*/
class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private Thread thread; // SurfaceView通常須要自己單獨的線程來播放動畫
private Canvas canvas;
private SurfaceHolder surfaceHolder;
private GameObject obj;
public MySurfaceView(Context c) {
super(c);
this.surfaceHolder = this.getHolder();
this.surfaceHolder.addCallback(this);
this.obj = new GameObject();
}
@Override
public void run() {
while (true) {
obj.getNextPos();
canvas = this.surfaceHolder.lockCanvas(); // 通過lockCanvas加鎖並得到該SurfaceView的畫布
canvas.drawColor(Color.BLACK);
obj.drawSelf(canvas); // 把SurfaceView的畫布傳給物件。物件會用這個畫布將自己繪制到上面的某個位置
this.surfaceHolder.unlockCanvasAndPost(canvas); // 釋放鎖並提交畫布進行重繪
try {
Thread.sleep(10); // 這個就相當於幀頻了,數值越小畫面就越流暢
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
Toast.makeText(getApplicationContext(), "SurfaceView已經銷毀", Toast.LENGTH_LONG).show();
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
Toast.makeText(getApplicationContext(), "SurfaceView已經創建", Toast.LENGTH_LONG).show();
this.thread = new Thread(this);
this.thread.start();
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// 這里是SurfaceView發生變化的時候觸發的部分
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MySurfaceView(getApplicationContext())); // 別忘了開始的時候加載我們加工好的的SurfaceView
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
假設轉載請注明出處:http://blog.csdn.net/gophers
