1.AndroidUI管理系统的层级关系
1.1.主要对象介绍
(1)PhoneWindow:Android系统中最基本的窗口系统,继承自Window类,负责管理界面显示以及事件响应,是Activity和View系统交互的接口。
(2)DecorView:PhoneWindow中的起始点View,继承于View类,作为整个视图容器使用,用于设置窗口属性,本质上是一个FrameLayout。
(3)ViewRoot:在Activity启动时创建,负责管理布局,渲染窗口UI
1.2.ViewRoot与DecorView
ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程均通过ViewRoot来完成,在ActivityThread中,Activity被创建以后,会将DecorView添加到Window中,同时创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联(通过ViewRoot的setView方法)。
1.3.View的绘制流程
View的绘制流程是从ViewRoot的PerformTraversals方法开始的,经过measure,layout,draw三个过程最终将一个View绘制出来。
- measure(测量):测量View的宽高。
- layout(布局):确定View在父布局中的放置位置。
-
draw(绘制):负责将View绘制在屏幕上
performTraversals会依次调用performMeasure,performLayout,performDraw三个方法,这三个方法分别完成*View的measure,layout,draw三大方法。measure方法中又会调用onMeasure方法,在此方法中会对所有元素进行measure过程,此时measure流程就从父容器传递到子元素中,一次measure过程就完成了,子元素会重复父容器的measure过程如此反复就完成了整个view树的遍历。performLayout和performDraw的传递过程是在draw方法中通过dispatchDraw方法完成的。
(1)measure过程
measure过程分为两类,普通的View通过measure方法后就完成了它的测量过程,而ViewGroup除了自己的测量过程外,还会遍历所有子元素的measure方法,子元素再递归执行。
View的measure过程:是一个final方法,不可重写,在该方法中会调用onMeasure方法。onMeasure方法中会调用getDefaultSize方法返回测量后的View的大小,getDefault方法中会调用getSuggestedWidth和getSuggestedHeight方法(这两个方法在没有指定背景的情况下,返回的是minSize这一属性对应的值,在指定了背景的情况下,返回的是背景的getMinimumWidth/getMiniumHeight方法对应的值)。View的宽高由specSize决定,因此直接继承自View的控件需要重写onMeasure方法并设置wrap_content时自身的大小否则在布局文件中使用wrap_content就相当于使用match_parent。
ViewGroup的measure过程:ViewGroup除了完成自己的measure过程,还会遍历调用子元素的measure方法,然后子元素再递归执行。ViewGroup是一个抽象类,因此没有重写View的onMeasure方法,但他提供了measureChildren方法,他会取出子元素的LayoutParams,通过getChildMeasureSpec方法创建子元素MeasureSpec,然后传递给View的measure方法进行测量,ViewGroup没有定义具体的测量过程具体测量过程的onMeasure需要子类来实现,由于子类特性可能很大不同,无法统一处理。(一般获取View宽高可以在onWindowFocusChanged中获取,此时View已经测量完毕了)
(2)layout过程
Layout的作用是ViewGroup用来确定子元素的位置,当ViewGroup的位置被确定后,他在onLayout中会遍历所有子元素并调用其layout方法,在layout方法中onLayout方法又会被调用,layout方法确定view的位置,onLayout方法确定所有子元素位置。
layout方法的流程:首先通过setFrame方法来设定View的四个顶点的位置即初始化mLeft,mRight,mTop,mBottom这四个值,View的四个顶点确定,在父容器中的位置也就确定了,接着会调用onLayout方法,父容器来确定子元素的位置,onLayout方法的实现和具体布局有关。
(3)draw过程
view的绘制过程主要分为四步
- 绘制背景background.draw(canvas)
- 绘制自己onDraw
- 绘制children(dispatchDraw)
-
绘制装饰(onDrawScollBars)
view绘制过程的传递是通过diapatchDraw实现,该方法会遍历调用所有子元素的draw方法这样draw方法就能一层层传递下去了。
setWillNotDraw:如果一个view不需要绘制任何内容,在我们设定这个标记为true时,系统会做相应的优化,view一般不启用,viewgroup默认启用自定义控件继承自viewGroup并且不具备绘制功能时,可以开启这个标记方便系统进行优化
1.4.MeasureSpec
MeasureSpec代表一个32位的int值,高两位代表SpecMode(测量模式),低30位代表SpecSize(在某种测量模式下的规格大小)
SpecMode有三类:
- UNSPECIFIED:父容器不对view有任何限制要多大给多大,通常用于系统内部
- EXACTLY:父容器已经检测出view所需要的精确大小,此时view最终大小就是SpecSize所指定的值,对用于LayoutParams中的match_parent和具体数值这两种模式
-
AT_MOST:父容器指定了可用大小SpecSize,View的大小不能大于这个值,具体是什么值要看不同的view的具体实现,对应于LayoutParams中的wrap_content
对于普通view,其MeasureSpec由父容器的MeasureSpec由父容器的MeasureSpec和自身的LayouParams共同决定 - View采用固定宽高时:不管父容器的MeasureSpec是什么,View的MeasureSpec都是精确模式并且大小遵循LayoutParams中的大小
- 当view的宽高是match_parent时:如果父容器是精准模式,那么view也是精准模式并且其大小是父容器的剩余控件。
- 父容器是最大模式:view是最大模式且大小不会超过父容器的剩余空间
- view宽高是wrap_content时:不管父容器的模式是精准还是最大化,view的模式总是最大化,并且大小不超过父容器的剩余空间。