- View的绘制过程
- measure过程
- layout过程
- draw过程
- 常见的方法
- View绘制的工具类CanvasPaintBitmap
- SurfaceView
- 使用SurfaceView的过程
- 绘图原理
- SurfaceView双缓存
- View和SurfaceView比较
View的绘制过程
View的绘制流程主要由三部分组成,依次是:measure,layout,draw。
measure过程
测量View宽高的过程。
为整个View树计算实际的大小,即设置实际的高(对应属性:mMeasuredHeight)和宽(对应属性:mMeasureWidth),每个View的实际宽高都是由父控件和本身决定的。
从ViewRoot开始调用measure()方法去计算View树大小,然后回调子View(ViewGroup)的onMeasure()方法,以此类推:
1. onMeasure()中通过setMeasuredDimension()方法去设置实际的高 (mMeasuredHeight)和宽(mMeasureWidth) 。
2. 2.如果是ViewGroup的话,还要遍历所有子View进行measure()过程,这通过measureChildWithMargins()方法实现,它其实就是调用了每个子View(或ViewGroup)的measure()方法。
一般在进行自定义View开发时,需要复写此方法自定义测量过程。
如果不复写此onMeasure方法,则默认使用getDefaultSize方法得到的值。
在View测量时会传入一个变量MeasureSpec,它的作用就是告诉View应该以哪一种模式测量这个View,SpecMode有三种模式:
- UNSPECIFIED:表示父容器不对View有任何限制,这种模式主要用于系统内部多次Measure的情况,不需要过多关注
- AT_MOST:父容器已经指定了大小,View的大小不能大于这个值,相当于布局中使用的wrap_content模式
- EXACTLY:表示View已经定义了精确的大小,使用这个指定的精确大小specSize作为该View的大小,相当于布局中我们指定了66dp这种精确数值或者match_parent模式
layout过程
确定View在父容器的位置的过程。
根据子视图的大小以及布局参数将View树放到合适的位置上:从host.layout()开始View树的布局,然后回调View/ViewGroup类中的layout()方法。
1. 1.layout方法会设置该View位于父View的坐标轴(即mLeft,mTop,mLeft,mBottom,通过setFrame() )接下来回调onLayout()方法。
2. 如果是ViewGroup类型,需要遍历每个childView(子View或ViewGroup),调用它们的的layout()方法去设置它的坐标值。
draw过程
绘制View的过程。
从ViewRoot对象的performTraversals()调用draw()方法起,开始绘制View树。(只绘制“需要绘制的”,通过参考View的标志位:DRAWN)
1. View调用onDraw()方法绘制本身
2. ViewGroup调用dispatchDraw()方法绘制子视图
常见的方法
-
invalidate()方法:
请求重绘View树,即draw()过程,假如视图发生大小没有变化就不会调用layout()过程。
除方法本身,如下方法会引起invalidate():setSelection()、setVisibility()、setEnabled()
-
requestLayout()方法:
对View树重新measure和layout的过程,不会调用draw过程,也不会重新绘制任何视图包括该调用者本身。
在View的可视状态通过setVisibility()在由INVISIBLE/VISIBLE 转换为GONE时,会间接调用requestLayout() 和invalidate()方法。
-
requestFocus()方法:
会调用draw()过程重绘。
View绘制的工具类:Canvas、Paint、Bitmap
见之前写的这篇文章:
http://blog.csdn.net/a565815942/article/details/52002929
SurfaceView
使用SurfaceView的过程
- 继承SurfaceView
- 在初始化的时候拿到SurfaceHolder并给SurfaceHolder设置Callback
- 实现surfaceCreated() surfaceChanged() surfaceDestroyed()方法
- 在surfaceCreated中开启线程,在线程里通过surfaceHolder的lockCanvas 和 unlockCanvasAndPost方法锁定Canvas并绘图
- 在surfaceDestroyed中结束这个绘图线程
绘图原理
Android的应用程序是通过SurfaceFlinger服务来绘制自己的UI的。
每个窗口在SurfaceFlinger服务中都对应有一个Layer,用来描绘它的绘图表面。
而SurfaceView额外对应有一个Layer。SurfaceView在其宿主Activity窗口上设置了一块透明区域,以便它的UI可以对用户可见。
因此SurfaceView具体实现过程
- 创建绘图表面
- 宿主窗口设置一块透明
- 绘制
SurfaceView双缓存
SurfaceView在更新视图时用到了两张Canvas,一张frontCanvas和一张backCanvas,每次实际显示的是frontCanvas,backCanvas存储上一次更改前的视图.
当使用lockCanvas()获取画布时,得到的实际上是backCanvas而不是正在显示的frontCanvas,之后你在获取到的backCanvas上绘制新视图,再unlockCanvasAndPost(canvas)此视图,那么上传的这张canvas将替换原来的frontCanvas作为新的frontCanvas,原来的frontCanvas将切换到后台作为backCanvas。
View和SurfaceView比较
View:必须在UI的主线程中更新画面,用于被动更新画面。
surfaceView:UI线程和子线程中都可以。在一个新启动的线程中重新绘制画面,主动更新画面。
UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。
当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步,涉及到线程同步。