SurfaceView与View的不同主要体现在一下几点
- View主要适用于主动更新的情况下,而SurfaceView主要适用于被动更新,例如频繁的刷新。
- View在主线程中对画面进行刷新,而SurfaceView通常会通过一个子线程来进行页面的刷新。
- View在绘图时没有使用双缓冲机制,而SurfaceView在底层实现机制中就已经实现了双缓冲机制。
总结成一句话就是:如果你的自定义View需要频繁刷新,或者刷新时数据处理量较大,那么你就可以考虑用SurfaceView来取代View了。
使用时,大多情况下,可以嵌套如下模板:
public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {
//SurfaceHolder
private SurfaceHolder mHolder;
//用于绘图的Canvas
private Canvas mCanvas;
//子线程标志位
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() {
//初始化SurfaceHolder
mHolder = getHolder();
//注册回调方法
mHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
//mHolder.setFormat(PixelFormat.OPAQUE);
}
/**
* 创建
*
* @param holder
*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true;
new Thread(this).start();
}
/**
* 改变
*
* @param holder
* @param format
* @param width
* @param height
*/
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
/**
* 销毁
*
* @param holder
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing = false;
}
@Override
public void run() {
while (mIsDrawing) {
draw();
}
}
private void draw() {
try {
//通过lockCanvas可以得到当前的Canvas对象。
mCanvas = mHolder.lockCanvas();
//draw something
} catch (Exception e) {
} finally {
if (mCanvas != null) {
//试用该方法unlockCanvasAndPost讲画布内容进行提交
//将unlockCanvasAndPost方法放到finally中,保证每次都能讲内容提交
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
利用这个模板,我们尝试绘制如下正弦曲线:
实现如下:
public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {
//SurfaceHolder
private SurfaceHolder mHolder;
//用于绘图的Canvas
private Canvas mCanvas;
//子线程标志位
private boolean mIsDrawing;
private int x;
private int y;
private Path mPath;
private Paint mPaint;
private static final String TAG = "SurfaceViewTemplate";
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() {
//初始化SurfaceHolder
mHolder = getHolder();
//注册回调方法
mHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
//mHolder.setFormat(PixelFormat.OPAQUE);
mPath = new Path();
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10);
}
/**
* 创建
*
* @param holder
*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true;
mPath.moveTo(0, 400);
new Thread(this).start();
}
/**
* 改变
*
* @param holder
* @param format
* @param width
* @param height
*/
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
/**
* 销毁
*
* @param holder
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing = false;
}
@Override
public void run() {
while (mIsDrawing) {
draw();
x += 1;
y = (int) (100 * Math.sin(x * 2 * Math.PI / 180) + 400);
mPath.lineTo(x, y);
}
}
private void draw() {
try {
//通过lockCanvas可以得到当前的Canvas对象。
mCanvas = mHolder.lockCanvas();
//SurfaceView背景
mCanvas.drawColor(Color.WHITE);
mCanvas.drawPath(mPath, mPaint);
} catch (Exception e) {
} finally {
if (mCanvas != null) {
//试用该方法unlockCanvasAndPost讲画布内容进行提交
//将unlockCanvasAndPost方法放到finally中,保证每次都能讲内容提交
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
}
最后在xml中引用
<com.example.myview.myview.SurfaceViewTemplate
android:layout_width="match_parent"
android:layout_height="match_parent" />
再绘制如手写板的效果:
实现如下:
public class SurfaceViewCanvas extends SurfaceView implements SurfaceHolder.Callback, Runnable {
//SurfaceHolder
private SurfaceHolder mHolder;
//用于绘图的Canvas
private Canvas mCanvas;
//子线程标志位
private boolean mIsDrawing;
private int x;
private int y;
private Path mPath;
private Paint mPaint;
private static final String TAG = "SurfaceViewTemplate";
public SurfaceViewCanvas(Context context) {
super(context);
initView();
}
public SurfaceViewCanvas(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public SurfaceViewCanvas(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
//初始化SurfaceHolder
mHolder = getHolder();
//注册回调方法
mHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
//mHolder.setFormat(PixelFormat.OPAQUE);
mPath = new Path();
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10);
}
/**
* 创建
*
* @param holder
*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true;
new Thread(this).start();
}
/**
* 改变
*
* @param holder
* @param format
* @param width
* @param height
*/
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
/**
* 销毁
*
* @param holder
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing = false;
}
@Override
public void run() {
long start = System.currentTimeMillis();
while (mIsDrawing) {
draw();
}
long end = System.currentTimeMillis();
//50-100
if(end-start<100){
try {
Thread.sleep(100-(end-start));
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
private void draw() {
try {
//通过lockCanvas可以得到当前的Canvas对象。
mCanvas = mHolder.lockCanvas();
//SurfaceView背景
mCanvas.drawColor(Color.WHITE);
mCanvas.drawPath(mPath, mPaint);
} catch (Exception e) {
} finally {
if (mCanvas != null) {
//试用该方法unlockCanvasAndPost讲画布内容进行提交
//将unlockCanvasAndPost方法放到finally中,保证每次都能讲内容提交
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
@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;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
}