View通过刷新来重绘视图,android系统通过发出VSYNC信号来进行屏幕重绘,刷新间隔时间为16ms,如果在16ms内view完成了你所需要执行的所有操作,那么用户在视觉上就不会出现卡顿感觉,否则会导致画面卡顿。自定义view中经常会出现 The Application may be doing too munch work on its main thread 这样的警告日志。
这种警告的产生,多半是因为在绘制过程中处理逻辑太多造成的。
SurfaceView 和View 的差别。
1.View 主要适应于主动更新的情况,而SurfaceView适应于被动更新,例如频繁的刷新。
2.View在主线程中对画面进行刷新,而SurfaceView通常会通过一个子线程进行页面的刷新。
3.View在绘图时没有使用双缓冲机制,而SurfaceView底层实现机制已经实现了双缓冲机制。
所以,如果你的自定义view需要频繁的刷新,或者刷新时数据处理量比较大,就考虑用SurfaceView.
示例一:一个自动的sin图像绘制
使用步骤:
1.创建一个类继承SurfaceView 然后实现SurfaceHolder.Callback和Runnable,重写相应的方法,添加构造函数。
2.定义全局变量 SurfaceHolder,Canvas,子线程标志位 isDrawing,画笔Paint,绘制路径Path.
3.定义一个方法初始化参数
4.定义一个draw()方法,通过surfaceholder.lockCanvas()获取一个canvas,通过canvas绘制想要绘制的view
5.在重写的run方法里通过调用draw方法进行绘制。
6.在surfaceCreated()方法里将子线程开启。将子线程标志位定义为true
7.在surfaceDestroyed()方法里将子线程标志位定义为false。
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable { private SurfaceHolder mHolder; // 用于绘制的canvas private Canvas mCanvas; // 子线程标志位 private boolean mIsDrawing; private Path path; private Paint paint; public MySurfaceView(Context context) { super(context); initParams(); } public MySurfaceView(Context context, AttributeSet attrs) { super(context, attrs); initParams(); } public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initParams(); } private void initParams() { // SurfaceHolder mHolder = getHolder(); mHolder.addCallback(this); setFocusable(true); setFocusableInTouchMode(true); this.setKeepScreenOn(true); path = new Path(); paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.RED); paint.setStrokeWidth(10); paint.setStrokeCap(Paint.Cap.ROUND); paint.setStyle(Paint.Style.STROKE); paint.setStrokeJoin(Paint.Join.ROUND); } /** * 开启子线程进行绘制 * * @param surfaceHolder */ @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { mIsDrawing = true; new Thread(this).start(); } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { mIsDrawing = false; } private int x = 0; private int y = 0; /* 子线程使用一个while(isDrawing)来循环不停的绘制 * 通过lockCanvas来获取一个Canvas对象 * 通过unlockCanvasAndPost(canvas)对画布内容进行提交。 * 提交步骤最好放在finally,保证每次都能将内容提交。 * */ @Override public void run() { path.moveTo(0, 400); while (mIsDrawing) { draw(); x += 1; y = (int) (100 * Math.sin(x * 2 * Math.PI / 180) + 400); path.lineTo(x, y); } } private void draw() { try { // 获取一个canvas对象。注意,这里的canvas对象还是继续上次的canvas,之前的绘图操作都会保留,如果需要擦出,可以在绘制前 // 通过 canvas.drawColor()进行清屏操作。 // canvas = mHolder.lockCanvas(); mCanvas = mHolder.lockCanvas(); mCanvas.drawColor(Color.WHITE); mCanvas.drawPath(path, paint); } catch (Exception e) { e.printStackTrace(); } finally { if (mCanvas != null) { mHolder.unlockCanvasAndPost(mCanvas); } } } }
示例二,一个简易画板
public class DrawSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable { private SurfaceHolder mHolder; private boolean isDrawing; private Canvas canvas; private Path path; private Paint paint; public DrawSurfaceView(Context context) { super(context); initParams(); } public DrawSurfaceView(Context context, AttributeSet attrs) { super(context, attrs); initParams(); } public DrawSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initParams(); } private void initParams() { // 初始化SurfacHolder mHolder = getHolder(); mHolder.addCallback(this); setFocusable(true); setFocusableInTouchMode(true); this.setKeepScreenOn(true); path = new Path(); paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(10); } @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 surfaceCreated(SurfaceHolder surfaceHolder) { isDrawing = true; new Thread(this).start(); } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { isDrawing = false; } @Override public void run() { long start = System.currentTimeMillis(); while (isDrawing) { draw(); } long end = System.currentTimeMillis(); if (end - start < 100) { try { Thread.sleep(100 - (end - start)); } catch (Exception e) { e.printStackTrace(); } } } private void draw() { try { canvas = mHolder.lockCanvas(); canvas.drawColor(Color.GREEN); canvas.drawPath(path, paint); } catch (Exception e) { } finally { if (canvas != null) { mHolder.unlockCanvasAndPost(canvas); } } } }