1.View的加载流程
1.1.创建顶层容器DecorView,DecorView继承FragmentLayout,是一个容器。
1.2.根据设置的主题在顶层容器DecorView加载基础布局(如R.simple_layout.xml),基础布局中包含了一个id为R.id.content的FragmentLayout。
1.3.将ContentView添加到FragmentLayout中。也就是把我们的编写的布局添加到FragmentLayout中
视图结构图
2.View的绘制流程
我们的XML布局加载到DeocrView之后,将DeocrView添加到Windwo窗口进行绘制,总体流程如下:
1.ActivityThread:handleResumeActivity()(入口)
2.WindowManagerImpl:addView()
3.WindowManagerGlobal:addView()
4.ViewRootImpl:setView()
5.ViewRootImpl:requestLayout()(checkThread(),scheduleTraversals)
6.ViewRootImpl:doTraversal()
7.ViewRootImpl:performTraversals()
//绘制流程的三大步
8.performMeasure()——performLayout()——performDraw()
总结:
1.绘制的入口
ActivityThread.handleResumeActivity——WindowManagerImpl.addView——WindowManagerGlobal.addView
2.绘制类的方法
ViewRootImpl.setView(关联decorview和布局属性layoutParams)——ViewRootImpl.requestLayout(申请绘制)——ViewRootImpl.doTraversal——ViewRootImpl.performTraversals(执行绘制流程)
3.绘制的三大步骤
测量:ViewRootImpl.performMeasure(执行测量)
布局:ViewRootImpl.performLayout(执行布局)
绘制:ViewRootImpl.performDraw(执行绘制)
2.1.测量(view.measure——view.onMeasure)
2.1.1.目的
确定View的宽高
2.1.2.MeasureSpec的概念
View的宽高的测量依据测量规格(MeasureSpec)进行测量,MeasureSpec 是 32位 2进制 int 数值,高二位 测量模式(SpecMode),低30位 测量尺寸(SpecSize)
SpecMode
UNSPECIFIED :父容器不对View做任何限制,系统内部使用
EXACTLY:父容器监测出View的大小就是SpecSize,对应LayoutParams 的match_parent 和 固定大小
AT_MOST:父容器会指定一个可用大小,View的大小不能超过这个值,对应LayoutParams的 wrap_content
2.1.3.测量步骤
2.1.3.1.确定顶层View(DecorView)的测量规格(MeasureSpec)
DecorView的测量规格(MeasureSpec)由窗口的大小 windowSize和自身的LayoutParams设置属性有关
(1)LayoutParams的属性为 MATCH_PARENT
SpecMode:MeasureSpec.EXACTLY
SpecSize:windowSize
(2)LayoutParams的属性为WRAP_CONTENT
SpecMode:MeasureSpec.AT_MOST
SpecSize:windowSize
(3)否则
SpecMode:MeasureSpec.EXACTLY
SpecSize:rootDimension自身的LayoutParams设置大小)
2.1.3.2.ViewGroup的测量
(1)onMeasure(遍历所有子View进行测量)
子View的MeasureSpec的确定由父容器的MeasureSpec(顶层父容器就是DecorView)和自身的LayoutParams设置属性有关,如图
再根据所有子View的宽高大小,计算出maxWidth ,maxHeight
如:FrameLayout
(2)setMeasuredDimension——setMeasuredDimensionRaw(确定自身的宽高)
根据以上算出的maxWidth 和maxHeight,再结合一些其他因素(如padding,设置最小宽高,背景等)最后算出自身控件的宽高进行保存。
2.1.3.3.View的测量
onMeasure——setMeasuredDimension——getDefaultSize——setMeasuredDimensionRaw ,
根据父容器MeasureSpec和自身的LayoutParams设置属性得到自身的MeasureSpec,然后再调用getDefaultSize,和setMeasuredDimensionRaw确定自身的大小。
注意:
2.2.布局(view.layout——view.onLayout)
调用view.layout方法,确定自身的位置,即确定mLeft,mTop,mBottom,mRight。然后调用setFrame方法确定位置。
如果是ViewGroup的话,在layout方法里调用 onLayout方法,然后在onLayout方法里递归遍历对子view进行布局,确定各个子view的位置。
和以上view的测量对比,我们知道,测量是先对所有的子view进行测量,然后再对容器进行测量。布局是先确定容器的布局,然后再对子view进行布局。
2.2.绘制(view.draw)
- Draw the background(绘制背景drawBackground(canvas);)
- If necessary, save the canvas' layers to prepare for fading(如果需要,对涂层进行保存)
- Draw view's content(绘制自己的内容 onDraw(canvas))
- Draw children(绘制子view dispatchDraw(canvas)递归遍历子view,调用子view的draw方法)
- If necessary, draw the fading edges and restore layers(必要时,绘制褪色边缘并恢复图层)
- Draw decorations (scrollbars for instance )(绘制装饰,滚动条等等 onDrawForeground)
2.2.小结
自定义view开发的时候:
1.重写onMeasure,进行测量
2.重写onLayout方法(如果是容器话,需要重写)
3.重写onDraw方法,进行绘制content(可选,如果绘制的是容器,不需要绘制内容,则不需要)