SurfaceView是View的继承结构中一个比较特殊的子类,它的作用是提供一个第二线程来完成图形的绘制。因此应用程序不需要等待View的图形绘制,第二线程会异步完成图形的绘制。
SurfaceView实现的步骤:
- 继续SurfaceView并实现SurfaceHolder.Callback接口,该接口提供了SurfaceView创建、属性发生变化、销毁的时间点,那么你可以在适当的时间点完成具体的工作。
- 在SurfaceView初始化的时候调用SurfaceView.getHolder()方法获取一个SurfaceHolder,SurfaceHolder用于管理SurfaceView的工作过程。为了让SurfaceHolder起作用,必须为SurfaceHolder添加回调方法(即第一步实现的SurfaceHolder.Callback):SurfaceHolder.addCallBack(SurfaceHolder.Callback);
- 在SurfaceView内创建第二线程的内部类(暂命名为SecondThread),它的主要任务是完成Canvas的图形绘制。为了能让SecondThread获得Canvas实例,必须给SecondThread传递在步骤二中获得的SurfaceHolder。现在就可以通过SurfaceHolder.lockCanvas()方法得到Canvas实例,并在Canvas上绘制图形。当图形绘制完成后,必须马上调用SurfaceHolder.unlockCanvasAndPost()为Canvas解锁,使其他线程可以使用该画布。
有几个注意点:
- 每一次通过SurfaceHolder获取的Canvas都会保持上一次绘制的状态。如果需要重新绘制图形,可以通过调用Canvas.drawColor()或Canvas.drawBitmap()来擦除上次遗留的图形。
- 并不一定只用第二线程来绘制图形,也可以开启第三,第四个线程来绘制图形。
- 注意线程安全。
- 不需要像View一样,调用invalidate()方法来指示图形的刷新。
SurfaceView的一个范例:package com.sin90lzc.android.sample; import java.util.ArrayList; import java.util.Collections; import java.util.List; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; public class CanvasView extends SurfaceView implements SurfaceHolder.Callback { public static class Point { private float x; private float y; public Point(float x, float y) { this.x = x; this.y = y; } public float getX() { return x; } public void setX(float x) { this.x = x; } public float getY() { return y; } public void setY(float y) { this.y = y; } public Point nextPoint(Orien o) { float tempX = x; float tempY = y; switch (o) { case UP: tempY = y - LINE_LENGTH; break; case DOWN: tempY = y + LINE_LENGTH; break; case LEFT: tempX = x - LINE_LENGTH; break; case RIGHT: tempX = x + LINE_LENGTH; break; case UNKNOWN: break; } return new Point(tempX, tempY); } } enum Orien { UP, LEFT, DOWN, RIGHT, UNKNOWN } public static class DrawThread extends Thread { private List<Point> points = Collections .synchronizedList(new ArrayList<Point>()); private boolean mRun; private Paint mPaint; private Orien curOrien; public synchronized void setRun(boolean run) { this.mRun = run; notifyAll(); } public synchronized boolean getRun() { while (!mRun) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } return mRun; } //当按上下左右键时,生成相应的点坐标 private synchronized boolean doKeyDown(int KeyCode, KeyEvent event) { synchronized (holder) { Point p = null; switch (KeyCode) { case KeyEvent.KEYCODE_DPAD_UP: if (curOrien != Orien.DOWN) { curOrien = Orien.UP; p = curPoint.nextPoint(curOrien); } break; case KeyEvent.KEYCODE_DPAD_DOWN: if (curOrien != Orien.UP) { curOrien = Orien.DOWN; p = curPoint.nextPoint(curOrien); } break; case KeyEvent.KEYCODE_DPAD_LEFT: if (curOrien != Orien.RIGHT) { curOrien = Orien.LEFT; p = curPoint.nextPoint(curOrien); } break; case KeyEvent.KEYCODE_DPAD_RIGHT: if (curOrien != Orien.LEFT) { curOrien = Orien.RIGHT; p = curPoint.nextPoint(curOrien); } break; default: curOrien = Orien.UNKNOWN; } if (p != null) { curPoint = p; points.add(p); setRun(true); } Log.i(LOG_TAG, curOrien.toString()); } return true; } //当释放按键时,停止绘图 private synchronized boolean doKeyUp(int KeyCode, KeyEvent event) { synchronized (holder) { setRun(false); curOrien = Orien.UNKNOWN; } return true; } SurfaceHolder holder; private Point curPoint; public DrawThread(SurfaceHolder holder) { this.holder = holder; mPaint = new Paint(); mPaint.setColor(Color.GREEN); curPoint = new Point(50, 50); points.add(curPoint); } public void resetPoint() { } private void doDraw(Canvas canvas) { for (int i = 0; i + 1 < points.size(); i += 1) { Point lp = points.get(i); Point np = points.get(i + 1); canvas.drawLine(lp.getX(), lp.getY(), np.getX(), np.getY(), mPaint); } } @Override public void run() { Canvas canvas = null; while (getRun()) { try { canvas = holder.lockCanvas(); synchronized (holder) { doDraw(canvas); } } finally { holder.unlockCanvasAndPost(canvas); setRun(false); } } } } private DrawThread thread; public static final String LOG_TAG = "CanvasView"; private static final int LINE_LENGTH = 30; public CanvasView(Context context) { super(context); } public CanvasView(Context context, AttributeSet attrs) { super(context, attrs); //SurfaceView由SurfaceHolder管理 SurfaceHolder holder = getHolder(); holder.addCallback(this); thread = new DrawThread(holder); thread.start(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { return thread.doKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { return thread.doKeyUp(keyCode, event); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.i(LOG_TAG, "surfaceChanged"); thread.resetPoint(); thread.setRun(true); } @Override public void surfaceCreated(SurfaceHolder holder) { Log.i(LOG_TAG, "surfaceCreated"); thread.resetPoint(); thread.setRun(true); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.i(LOG_TAG, "surfaceDestroyed"); thread.setRun(false); } }
Notice:例子中,没一次按下方向键都得把所有坐标重新绘制一遍。如果只是绘制最后一次没绘制的点时,不知道为什么会变成虚线,有待解决。