我们知道View是通过刷新来重绘视图,系统通过发出VSSYNC
信号来进行屏幕的重绘,刷新的时间间隔是16ms
,如果我们可以在16ms以内将绘制工作完成,则没有任何问题,如果我们绘制过程逻辑很复杂,并且我们的界面更新还非常频繁,这时候就会造成界面的卡顿,影响用户体验,为此Android提供了SurfaceView
来解决这一问题。
如果View需要频繁的刷新,或者刷新的数据量比较大,就需要使用SurfaceView
优势
View中刷新频繁,Ondraw()会频繁调用,onDraw方法执行的时间过程会掉帧,出现页面卡顿。SurfaceView使用双缓冲技术 ,提高了绘制速度,可以缓解这一现象。
SurfaceView可以在子线程更新UI,不会阻塞主线程,提高了响应速度.
https://www.jianshu.com/p/b037249e6d31
啥是双缓冲技术? https://blog.csdn.net/guanguanboy/article/details/99715643
Android群英传 6.8
SurfaceView使用模板 大部分绘图都按照模板来
SurfaceViewTemplate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder .Callback , Runnable { private SurfaceHolder mSurfaceHolder; private Canvas mCanvas; private boolean mIsDrawing; public SurfaceViewTemplate (Context context) { this (context, null ); } public SurfaceViewTemplate (Context context, AttributeSet attrs) { this (context, attrs, 0 ); } public SurfaceViewTemplate (Context context, AttributeSet attrs, int defStyleAttr) { super (context, attrs, defStyleAttr); initView(); } @Override public void surfaceCreated (SurfaceHolder holder) { mIsDrawing = true ; new Thread(this ).start(); } @Override public void surfaceChanged (SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed (SurfaceHolder holder) { mIsDrawing = false ; } @Override public void run () { while (mIsDrawing){ draw(); } } private void draw () { try { mCanvas = mSurfaceHolder.lockCanvas(); mCanvas.drawColor(Color.WHITE); }catch (Exception e){ }finally { if (mCanvas != null ){ mSurfaceHolder.unlockCanvasAndPost(mCanvas); } } } private void initView () { mSurfaceHolder = getHolder(); mSurfaceHolder.addCallback(this ); setFocusable(true ); setKeepScreenOn(true ); setFocusableInTouchMode(true ); } }
正旋曲线
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 public class SurfaceViewSinFun extends SurfaceView implements SurfaceHolder .Callback , Runnable { private SurfaceHolder mSurfaceHolder; private Canvas mCanvas; private boolean mIsDrawing; private int x = 0 , y = 0 ; private Paint mPaint; private Path mPath; public SurfaceViewSinFun (Context context) { this (context, null ); } public SurfaceViewSinFun (Context context, AttributeSet attrs) { this (context, attrs, 0 ); } public SurfaceViewSinFun (Context context, AttributeSet attrs, int defStyleAttr) { super (context, attrs, defStyleAttr); mPaint = new Paint(); mPaint.setColor(Color.BLACK); mPaint.setStyle(Paint.Style.STROKE); mPaint.setAntiAlias(true ); mPaint.setStrokeWidth(5 ); mPath = new Path(); mPath.moveTo(0 , 100 ); initView(); } @Override public void surfaceCreated (SurfaceHolder holder) { mIsDrawing = true ; new Thread(this ).start(); } @Override public void surfaceChanged (SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed (SurfaceHolder holder) { mIsDrawing = false ; } @Override public void run () { long start = System.currentTimeMillis(); while (mIsDrawing){ drawSomething(); x += 1 ; y = (int )(100 * Math.sin(2 * x * Math.PI / 180 ) + 400 ); mPath.lineTo(x, y); } long end = System.currentTimeMillis(); if (end - start<100 ){ try { Thread.sleep(100 -(end-start)); } catch (InterruptedException e) { e.printStackTrace(); } } } private void drawSomething () { try { mCanvas = mSurfaceHolder.lockCanvas(); mCanvas.drawColor(Color.WHITE); mCanvas.drawPath(mPath, mPaint); }catch (Exception e){ }finally { if (mCanvas != null ){ mSurfaceHolder.unlockCanvasAndPost(mCanvas); } } } private void initView () { mSurfaceHolder = getHolder(); mSurfaceHolder.addCallback(this ); setFocusable(true ); setKeepScreenOn(true ); setFocusableInTouchMode(true ); } }
通过draw()方法所使用的逻辑时长来确定sleep的时长,这是一个非常通用的解决方案,每次绘制时间控制在100ms
画板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 class DrawingBoardView : SurfaceView, SurfaceHolder.Callback, Runnable { private var mCanvas: Canvas? = null private var mSurfaceHolder: SurfaceHolder? = holder private var mIsDrawing = false private val x = 0 private var y: Int = 0 private var mPaint: Paint? = Paint().apply { color = Color.BLACK style = Paint.Style.STROKE isAntiAlias = true strokeWidth = 5f } private var mPath: Path? = null constructor(context: Context) : this (context, null ) constructor(context: Context, attrs: AttributeSet?) : this (context, attrs, 0 ) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super (context, attrs, defStyleAttr) { } init { mSurfaceHolder?.addCallback(this ) isFocusable = true keepScreenOn = true isFocusableInTouchMode = true mPath = Path() } override fun surfaceChanged (holder: SurfaceHolder?, format: Int, width: Int, height: Int) { } override fun surfaceDestroyed (holder: SurfaceHolder?) { } override fun surfaceCreated (holder: SurfaceHolder?) { mIsDrawing = true Thread(this ).start() } override fun run () { while (mIsDrawing) { draw() } } private fun draw () { try { mCanvas = mSurfaceHolder?.lockCanvas() mCanvas?.drawColor(Color.WHITE) mPaint?.let { mPath?.let { it1 -> mCanvas?.drawPath(it1, it) } } } catch (e: Exception) { } finally { mCanvas?.let { mSurfaceHolder?.unlockCanvasAndPost(it) } } } override fun onTouchEvent (event: MotionEvent?) : Boolean { val x = event?.x val y = event?.y when (event?.action) { MotionEvent.ACTION_DOWN -> x?.let { if (y != null ) { mPath?.moveTo(it, y) } } MotionEvent.ACTION_MOVE -> x?.let { if (y != null ) { mPath?.lineTo(it, y) } } } return true } }