其实每个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