一、SurfaceView存在的意义:
一般开发中,可能View已经可以满足需求了。View是通过刷新重绘视图,Android系统通过发出VSYNC信号来进行屏幕的重绘,刷新时间间隔为16ms。如果在16ms内View完成了所需执行的所有操作,屏幕就不会卡顿;但是如果View执行的逻辑操作太多,例如游戏界面需要频繁刷新,这时16ms内完不成这些操作,屏幕就会发生卡顿(因为View的操作阻塞了主线程)。在自定义View的Log中出现了:“Skipped 47 frames!The application may be doing to much work on its main thread”,就是因为在图形的绘制过程中处理了过多的逻辑。这时就需要将这些逻辑放在子线程中,就需要使用SurfaceView。
二、View和SurfaceView的区别:
1、View主要适用于主动更新的情况下,SurfaceView主要适用于被动更新,例如频繁的刷新;
2、View在主线程中对画面进行刷新,SurfaceView通过一个子线程实现对页面的刷新;
3、View在绘图是没有使用双缓冲机制,SurfaceView在底层实现机制中就已经实现了双缓冲机制。
总结:自定义的View需要频繁刷新或者刷新数据量过大时,就可以考虑使用SurfaceView了。
三、SurfaceView的使用:
1、创建SurfaceView:继承SurfaceView并实现SurfaceHolder.Callback,Runnable:继承SurfaceView并实现SurfaceHolder.Callback,Runnable接口;
2、初始化SurfaceView:自定义SurfaceView时通常需要三个变量:
//SurfaceHolder
private SurfaceHolder mHolder;
//用于绘图的Canvas
private Canvas mCanvas;
//子线程标志位,用来控制子线程
private boolean mIsDrawing;
初始化:
mHolder = getHolder();
mHolder.addCallback(this);
3、使用SurfaceView:通过SurfaceView的lockCanvas()方法获取当前的Canvas。注意:获取到的Canvas还是上次的Canvas,而不是一个新的对象,之前的绘图操作会被保留,如果不需要,可以使用drawColor()进行清屏操作。
SurfaceView使用模板:
package com.mfc.view;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
* 使用SurfaceView的模板
* */
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 defStyle) {
super(context, attrs, defStyle);
initView();
}
public void initView(){
//初始化操作
mHolder = getHolder();
mHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
}
@Override
public void run() {
while (mIsDrawing) {
draw();
}
}
private void draw(){
try {
//获取画布
mCanvas = mHolder.lockCanvas();
//下面就可以开始画View了
} catch (Exception e) {
e.printStackTrace();
} finally {
//将提交画布的代码写在finally里面,保证每次都能将内容提交
if(mCanvas != null){
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
mIsDrawing = true;
new Thread(this).start();
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
// TODO Auto-generated method stub
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
mIsDrawing = false;
}
}
四、SurfaceView使用实例:
1、绘制正弦图像:
绘制图形跟View基本相同:
主要代码:
package com.mfc.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
public class SinView extends SurfaceView implements Callback, Runnable {
// SurfaceHolder
private SurfaceHolder mHolder;
// 用于绘图的Canvas
private Canvas mCanvas;
// 子线程标志位
private boolean mIsDrawing;
private Paint paint;
private Path path;
private int x = 0;
private int y = 0;
public SinView(Context context) {
super(context);
initView();
}
public SinView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public SinView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
public void initView() {
mHolder = getHolder();
mHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
paint = new Paint();
path = new Path();
//起点坐标
path.moveTo(0, 200);
}
@Override
public void run() {
while (mIsDrawing) {
draw();
x = x+1;
y = (int) (100 * Math.sin(x * 2 * Math.PI / 180 )+400);
path.lineTo(x, y);
}
}
private void draw() {
try {
// 获取画布
mCanvas = mHolder.lockCanvas();
// 下面就可以开始画View了
mCanvas.drawColor(Color.WHITE);
paint.setStyle(Style.STROKE);
//绘制路径
mCanvas.drawPath(path, paint);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 将提交画布的代码写在finally里面,保证每次都能将内容提交
if (mCanvas != null) {
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
mIsDrawing = true;
new Thread(this).start();
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
// TODO Auto-generated method stub
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
mIsDrawing = false;
}
}
效果图:
2、绘图板:
主要代码:
package com.mfc.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
public class MyView extends SurfaceView implements Callback, Runnable {
// SurfaceHolder
private SurfaceHolder mHolder;
// 用于绘图的Canvas
private Canvas mCanvas;
// 子线程标志位
private boolean mIsDrawing;
private Paint paint;
private Path path;
public MyView(Context context) {
super(context);
initView();
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
public void initView() {
mHolder = getHolder();
mHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
paint = new Paint();
path = new Path();
//起点坐标
path.moveTo(0, 200);
}
@Override
public void run() {
//在子线程中进行sleep,让绘图板不会一直不断地更新,用来节省系统资源
long start = System.currentTimeMillis();
while (mIsDrawing) {
draw();
}
long end = System.currentTimeMillis();
if(end - start <100){
try {
Thread.sleep(100-(end - start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void draw() {
try {
// 获取画布
mCanvas = mHolder.lockCanvas();
// 下面就可以开始画View了
mCanvas.drawColor(Color.WHITE);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(10);
mCanvas.drawPath(path, paint);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 将提交画布的代码写在finally里面,保证每次都能将内容提交
if (mCanvas != null) {
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:
//手指按下点坐标
path.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
//手指移动到的位置坐标
path.lineTo(x, y);
break;
case MotionEvent.ACTION_UP:
//手指松开
break;
}
return true;
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
mIsDrawing = true;
new Thread(this).start();
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
// TODO Auto-generated method stub
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
mIsDrawing = false;
}
}
效果图:
源码下载: