SurfaceView的一些事
1.为什么Android设计了SurfaceView
View可以满足大部分需求,但是在某些特殊时候也力不从心。
因为View是通过刷新来重绘视图,Android系统通过发出VSYNC信号来进行屏幕绘制,刷新间隔时间为16ms。
如果16ms内view完成了绘制过程,那么用户在视觉上不会感到卡顿。
但是在绘制过程中有太多的逻辑需要处理,16ms内无法完成绘制的时候,就需要SurfaceView来解决这一问题。
2.View与SurfaceView的区别
- View主要适用于主动更新的情况下,SurfaceView适用于被动更新的情况,例如频繁的刷新
- View在主线程中对画面进行刷新,SurfaceView通常会通过一个子线程来进行页面刷新
- View在绘制时没有使用双缓冲机制,SurfaceView在底层实现机制中实现了双缓冲机制
- 双缓冲机制解释:在一个阳光明媚的日子,你想将水池里的水换掉,而又找不到水管的时候,你就只能用木桶来灌满水池。当木桶被水龙头注满的,关掉水龙头,走到水池旁边,将水到进去,然后走回到水龙头旁边继续重复上述工作,如此往复直到将水池灌满。这就类似单缓冲工作过程。当你想将木桶里的水倒出的时候,你必须关掉水龙头。
- 现在假设你用两个木桶来做上面的工作。你会注满第一个木桶然后将第二个木桶换到水龙头下面,这样,在第二个水桶注满的时间内,你就可以将第一个木桶里面的水倒进水池里面,当你回来的时候,你只需要再将第一个木桶换下第二个注满水木桶,当第一个木桶开始注水的时候你就将第二个木桶里面的水倒进水池里面。重复这个过程直到水池被注满。很容易看得到用这种技术注满水池将会更快,同时也节省了很多等待木桶被注满的时间,而这段时间里你什么也做不了,而水龙头也就不用等待从木桶被注满到你回来的这段时间了。
- 当你雇佣另外一个人来搬运一个被注满的木桶时,这就有点类似于三个缓冲区的工作原理。如果将搬运木桶的的时间很长,你可以用更多的木桶,雇佣更多的人,这样水龙头就会一直开着注满木桶了。
- 在计算机图形学中,双缓冲是一种画图技术,使用这种技术可以使得画图没有(至少是减少)闪烁、撕裂等不良效果,并减少等待时间。
- 双缓冲机制的原理大概是:所有画图操作将它们画图的结果保存在一块系统内存区域中,这块区域通常被称作“后缓冲区(back buffer)”,当所有的绘图操作结束之后,将整块区域复制到显示内存中,这个复制操作通常要跟显示器的光栈束同步,以避免撕裂。双缓冲机制必须要求有比单缓冲更多的显示内存和CPU消耗时间,因为“后缓冲区”需要显示内存,而复制操作和等待同步需要CPU时间。
3.SurfaceView的使用
模板代码
//继承SurfaceView 类,实现SurfaceHolder.Callback 和 Runnable 接口
public abstract class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {
//定义以下三个成员变量
private SurfaceHolder mHolder; //SurfaceHolder
private Canvas mCanvas; //用于绘制的Canvas
private boolean mIsDrawing; //子线程绘制标志位
public SurfaceViewTemplate(Context context) {
super(context);
initView();
}
public SurfaceViewTemplate(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
//初始化
private void initView() {
mHolder = getHolder();
mHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
setKeepScreenOn(true);
onInit(mHolder);
}
//初始化时候回调
protected abstract void onInit(SurfaceHolder holder);
//当SurfaceView创建的时候,开启子线程进行绘制操作
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true;
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
//当SurfaceViewDestroy时候停止绘制操作
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing = false;
}
private void draw() {
try {
mCanvas = mHolder.lockCanvas();
realDraw(mCanvas);
} catch (Exception e) {
onError(e);
} finally {
if (mCanvas != null) {
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
//循环进行绘制流程
@Override
public void run() {
while (mIsDrawing) {
beforeDraw();
draw();
afterDraw();
}
}
//绘制之前回调
protected abstract void beforeDraw();
//绘制完毕回调
protected abstract void afterDraw();
//真正的绘制一些东西
protected abstract void realDraw(Canvas canvas);
//绘制时候发生异常回调
protected abstract void onError(Exception e);
}
绘画板Demo
public class DrawingBoardView extends SurfaceViewTemplate {
public DrawingBoardView(Context context) {
super(context);
}
public DrawingBoardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DrawingBoardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private Paint mPaint;
private Path mPath;
@Override
protected void onInit(SurfaceHolder holder) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPath = new Path();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPath.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
mPath.lineTo(x, y);
break;
}
return true;
}
@Override
protected void beforeDraw() {
}
@Override
protected void afterDraw() {
}
@Override
protected void realDraw(Canvas canvas) {
//canvas默认保存了上一次绘制的内容
//所以这里清空一下,重新开始绘制
canvas.drawColor(Color.WHITE);
//绘制路径
canvas.drawPath(mPath, mPaint);
}
@Override
protected void onError(Exception e) {
}
}