…
}
SurfaceView是派生自View的。也就是说用View实现的自定义控件都可以使用SurfaceView来实现。
下面我们用SurfaceView来实现一下捕捉用户的手势轨迹的示例:
public class SurfaceGesturePath extends SurfaceView {
…
private void init() {
// setWillNotDraw(false);
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(7);
mPaint.setColor(Color.BLUE);
mPath = new Path();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mPath.moveTo(x, y);
return true;
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
mPath.lineTo(x, y);
}
postInvalidate();
Log.d(“Rikka”,“invalidate”);
return super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(mPath, mPaint);
Log.d(“Rikka”,“onDraw”);
}
上面的代码的执行结果是一片黑屏。
为什么同样的代码,派生自View就可以画图,而SurfaceView却不行?
我们调用了上面打印的LOG,在SurfaceView上点击滑动时,发现日志如下:
虽然走了TouchEvent的postInvalidate()方法,但是压根就不会执行onDraw()。
这时候,我们的init()函数中有一行setWillNotDraw(false);被注释了,如果我们让它执行,会怎么样呢?
(2)setWillNotDraw(boolean willNotDraw)
这个函数存在在View类中,它主要用在View派生子类的初始化中,如果参数willNotDraw取true,则表示当前控件没有绘制内容。当屏幕重绘的时候,这个控件不需要绘制,所以在重绘的时候也不会调用该控件的onDraw()函数。
相反如果为false,每次重绘都要执行onDraw()。
可以看出setWillNotDraw()是一个优化策略,它让控件显式的告诉系统,在重绘重绘时,哪个控件需要重绘,哪个控件不需要重绘。这样可以大大提高重绘效率。
一般而言,对于LinearLayout、RelativeLayout而言,他们的主要功能布局其中的控件,所以它们本身是没有东西需要绘制的,所以它们的构造方法在显式的设置 setWillNotDraw(true)
之所以我们上面列子中SurfaceView一开始黑屏,不重绘,真是因为它也 默认的设置setWillNotDraw为true了
所以从这里也看出,SurfaceView的设计人员其实不想让我们通过重写onDraw()函数来绘制SurfaceView的控件函数。
(3)总结:
-
SurfaceView派生自View
-
当SurfaceView需要使用View的onDraw()函数来重绘控件时,需要在初始化的时候执行 setWillNotDraw(false)
-
View中的所有方法都是执行在UI线程的,所以并不建议SurfaceView重写View的onDraw方法来实现自定义控件,而要使用SurfaceView特有的双缓冲机制。
2、使用缓冲Canvas绘图
之前讲了SurfaceView是自带画布的,具有双缓冲技术。这是SurfaceView建议使用的绘图方式。那么我们应该如何拿到这块自画布来绘图呢?
SurfaceHolder surfaceHolder = getHolder();
Canvas canvas1 = surfaceHolder.lockCanvas();
//绘图操作
…
surfaceHolder.unlockCanvasAndPost(canvas1);
我们先通过surfaceHolder.lockCanvas()函数得到SurfaceView的自带缓冲画布,并将这个画布加锁,防止它被别的线程更改。
当绘制完后,我们通过surfaceHolder.unlockCanvasAndPost(canvas)来将缓冲画布释放,并将所画的内容更新到主线程的画布上,显示的显示在屏幕上。
Q:为什么得到画布时要加锁?
A:SurfaceView的缓冲