UI绘制流程
view是如何被添加到屏幕窗口上
-
创建顶层布局容器DecorView
-
在顶层布局中加载基础布局ViewGroup
-
将ContentView添加到基础布局中的FrameLayout中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8OkUqOxv-1680226736134)(/Users/ada/Documents/Android/学习资料/pic/UI绘制/p1.png)]
顶层View绘制过程
-
绘制入口
ActivityThread.handleResumeActivity -->WindowManagerImpl.addView(decorView, layoutparams) -->WindowManagerGlobal.addView()
-
绘制的类及方法
ViewRootImpl.setView(decorView,layoutparams,parentView) -->ViewTootImpl.requestLayout()-->scheduleTraversals() -->doTraversal()-->performTraversals()
-
绘制三大步骤
测量:ViewTootImpl.performMeasure() 布局:ViewTootImpl.performLayout() 绘制:ViewTootImpl.performDraw()}
UI绘制详细步骤
-
测量performMeasure,确定控件的宽和高
view.measure-->view.onMeasure-->view.setMeasureDimesion-->setMeasuredDimennsionRaw
MeasureSpec:32位int型,SpecMode前两位模式 SpecSize后30位尺寸,MODE_SHIFT30
public static final int UNSPECIFIED = 0 << MODE_SHIFT; //00000000000000000000000000000000 父容器不对View做任何限制,系统内部使用 public static final int EXACTLY = 1 << MODE_SHIFT; //01000000000000000000000000000000父容器检测出View的大小,View的大小就是SpecSize 对应LayoutParams match_parent和固定大小 public static final int AT_MOST = 2 << MODE_SHIFT; 10000000000000000000000000000000 父容器指定一个可用大小,View的大小不能超过这个值,对应LayoutParams wrap_content //MODE_MASK : 1100 0000 0000 0000 0000 0000 0000 0000 //~MODE_MASK :0011 1111 1111 1111 1111 1111 1111 1111 //mode + size —>MeasureSpec :(size & ~MODE_MASK) | (mode & MODE_MASK)
ViewRootImpl::getRootMeasureSpec确定measureSpec measureChild(parentSpec)
确定DecorView的MeasureSpec
一、DecorView的MeasureSpec由窗口大小和自身LayoutParams决定,遵守如下规则:
- LayoutParams.MATCH_PARENT:精确模式,窗口大小
- LayoutParams.WRAP_CONTENT:最大模式,最大为窗口大小
- 固定大小:精确模式,大小为LayoutParams的大小
二、View的MeasureSpec由父容器的MeasureSpec和自身的LayoutParams决定
childLayoutParams parantLayoutParams EXACTLY AT_MOST UNSPECIFIED dp/px EXACTLY(childSize) EXACTLY(childSize) EXACTLY(childSize) MATCH_PARENT EXACTLY(parentSize) AT_MOST(childSize) UNSPECIFIED(0) WRAP_CONTENT AT_MOST(parentSize) AT_MOST(parentSize) UNSPECIFIED(0)ViewGroup measure—>onMeasure (获取子控件的宽高)—>setMeasuredDimension—>setMeasuredDimensionRaw(保存自己的宽高) 测量过程总结:
ViewGroup : measure—>onMeasure(测量子控件的宽高) —>setMeasuredDimension—>setMeasuredDimensionRaw(保存自己的宽高)
View :measure—>onMeasure —>setMeasuredDimension—>setMeasuredDimensionRaw(保存自己的宽高)
-
布局performLayout
调用View.layout确定自身的位置,即确定mLeft,mTop,mRight,mBottom的值
如果是ViewGroup类型,需要调用onLayout确定子View的位置
view.layout-->view.onlayout
-
绘制performDraw
绘制背景 drawBackgroud
绘制自己 onDraw
绘制子View dispatchDraw
绘制前景,滚动条等装饰onDrawForeground
自定义View需要重写onMeasure —>onLayout(容器)—>onDraw(可选)
ViewRootImpl.draw(fullRedrawNeeded)-->ViewRootImpl.drawSoftware-->View.draw(Canvas)
View的测量 (确定DecorView的MeasureSpec)
DecorView的MeasureSpec由窗口大小和自身LayoutParams决定,遵守如下规则:
- LayoutParams.MATCH_PARENT:精确模式,窗口大小
- LayoutParams.WRAP_CONTENT:最大模式,最大为窗口大小
- 固定大小:精确模式,大小为LayoutParams的大小