Android View绘制过程源码解读
2017-11-16 14:07:22 7 举报
深入理解setContentView过程和View绘制过程
作者其他创作
大纲/内容
迭代过程
【步骤3】向父容器请求布局 mParent.requestLayout();
PhoneWindow#DecorView继承自FrameLayout(未重写measure());继承自ViewGroupt(未重写measure());继承自View;
调用child.layout方法,把布局流程从父容器传递到子元素,开启迭代过程
Activity#SetContentView()
View#onDraw(canvas)绘制View的内容
View#requestLayout
【步骤2.2】如果fullRedrawNeeded为真,则把dirty区域置为整个屏幕,表示整个视图都需要绘制。第一次绘制流程,需要绘制所有视图
layout()
父是否是ViewRootImpl
View#OnMeasure一般会被重写:ViewGroup重写以支持子控件测量;View重写以支持padding和wrap_content配置
setContentView过程
ViewRootImpl#performDraw【步骤1】开始
其中会调用
View#invalidate()View#postinvalidate()
根据子View的layout_gravity属性、子View的测量宽高、父容器的padding值、child.margin,和其他子控件累积占用的位置来确定子View的布局参数
更新累积的宽高
获取该activity所关联的window对象,DecorView对象,以及windowManager对象,并调用WindowManager的addView方法
draw判断是否需要重新Draw,并draw
(1)判断当前mPrivateFlags是否带有PFLAG_FORCE_LAYOUT强制布局标记 **比如调用View.requestLayout()会在mPrivateFlags中加入此标记**(2)判断当前widthMeasureSpec和heightMeasureSpec是否发生了改变
onLayout的实现:遍历子控件
ViewRootImpl#scheduleTraversals()该方法会向主线程发送一个“遍历”消息,最终会导致进入4.2.1.1
WindowManagerGlobal#addView
【步骤2.3.1】实例化Canvas对象,并锁定canvas区域,由dirty区域决定
【步骤2】按照业务需求,累积子控件要求自己的宽高,并更新 计划宽高被其他子控件占用的数值,以作为下次遍历的输入
循环过程
【步骤1】判断当前View树是否正在布局流程,如果是该请求会延迟到布局流程完成后或者绘制流程完成且下一次布局发现的时候再执行
【步骤1】根据View的标记位来判断该子View是否需要重绘,假如View没有任何变化,那么就不需要重绘
【步骤2】为当前view设置标记位 PFLAG_FORCE_LAYOUT
否
setMeasuredDimension()设置自己的 实际测量宽高
onMeasure()
onDraw()
View#drawBackground(canvas)对View的背景进行绘制
调用Activity所关联的PhoneWindow
layout判断是否需要重新Layout,并layout
ViewGroup#invalidateChildInParent
ViewRootImpl#scheduleTraversals()
View#.draw(Canvas canvas)DectorView调用的其实是View的draw
保存当前的图层信息(可跳过)
遍历所有子控件,为子控件开启测量宽高流程
onCreate()方法执行完毕
PhoneWindow#installDecor()生成DecorView,mContentRoot 和mContentParent
【步骤2】设置PFLAG_DIRTY标记位
ViewRootImpl#draw(fullRedrawNeeded);【步骤2】实际调用了ViewRootImpl#draw方法,并传递了fullRedrawNeeded参数,它的作用是判断是否需要重新绘制全部视图
每个View都需要重载该方法;ViewGroup一般不需要实现该方法,除非有特定效果
ViewRootImpl#setView()ViewRootImpl、DecorView和WMS会彼此关联,并调用4.2.1
PhoneWindow#SetContentView()
ViewRootImpl#invalidateChildInParent
View#invalidate
【步骤2.3.2】对canvas进行一系列的属性赋值
WindowManagerImpl#addView
mMeasureCache.put缓存 实际测量宽高
onLayout()
ViewGroup#invalidateChild迭代回溯父布局,计算绘制区域
VieRootImpl#performLayout
ActivityThread#handleResumeActivity关联decorView与ViewRoot,并开启View整个流程
View#requestLayout()
是
【步骤2.3】调用了ViewRootImpl#drawSoftware方法,并把相关参数传递进去
draw()
判断该子View是否可见或者是否处于动画中
measure()
ViewRootImpl#requestLayout其中会调用scheduleTraversals()方法来调度一次完成的绘制流程
递归式测量了整个ViewTree的实际大小,每个view都存有自己的实际测量宽高,该宽高为正式layout阶段提供建议(只是建议,具体如何布局onLayout过程控制)
View#layout【步骤2】这是个final方法
高
ViewRootImpl#performTraversals()
父是否是DecorView
是否有缓存
onDrawForeground(canvas)绘制View的装饰(例如:滚动条)
ViewRootImpl#performTraversals()开始View的测量、布局、绘制流程
4.3.2.1 画布生成过程
Activity创建过程
measure判断是否需要重新Measure,并measure
绘制View的褪色的边缘,类似于阴影效果(可跳过)
启动ActivityActivityThread#handleLaunchActivity
ViewRootImpl#requestLayout()
Activity#onCreate
遍历每个子控件
【步骤2.1】获取mDirty,该值表示需要重绘的区域;
利用缓存来显示
View#dispatchDraw(canvas)对View的子View进行绘制(如果有子View),实现迭代绘制过程
ViewGroup#OnMeasure使用View的
收藏
0 条评论
下一页