View 的绘制流程从ViewRoot的performTraversals方法开始的,它经过measure, layout , draw 三个过程才能最终将一个View 绘制出来,
其中measure用来测量View的宽高,
Layout用来确定View在父容器中放置的位置,
draw则负责将View绘制到屏幕上。
ViewRoot对应于ViewRootImpl类,连接WindowManager与DecorView的纽带,Viewd的三大流程均是通过ViewRoot完成的。在ActivityTread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联,看源码:
root = new ViewRootImpl(view.getContext(),display);
root.setView(view,wparams,panelParentView);
针对performTraversals的大致流程:
performTraversals回依次调用:
performMeasure
performLayout
performDraw
这三个方法分别完成顶级View的
measure,layout, draw这三大流程
,
其中在performTraversals中会调用measure方法,在measure方法中又会调
用onMeasure方法,在onMeasure方法中则会对所有子元素进行measure过
程,这个时候measure流程就从父容器传递到子元素中。这样就完成一次
measure过程。接着子元素会重复父容器的measure过程,如此反复就完成了整
个View树的遍历。同理perfromLayout和performDraw的传递过程与
performMeasure是类似的,唯一不同,performDraw的传递过程是在draw方
法中通过dispatchDraw来实现的。
measure过程决定了View的宽高,Measure完成后,可以通过
getMeasureWidth和getMeasureHeight获取View测量后的宽高,在几乎所
有情况下他等同于View的最中宽高,但是特设情况除外,Layout 过程决定了
View的四个顶点的坐标和实际的View的宽高,完成以后可以通过
getTop,getBottom,getLeft,getRight来拿到View的四个顶点位置,可以
通过通过getWidth 和 getHeight方法来拿到View的最终宽高。Draw过程则
决定了View的显示,只有draw方法完成以后View的内容才能呈现在屏幕上。
在测量过程中,系统会将View的LayoutParams根据父容器的规则转成对应的MeasureSpec,然后根据measureSpec来测量出View的宽高,这里的宽高是测量的宽高,不一定等于View的最终宽高。
那么MeasureSpec是什么呢?
它代表32位的int值,高2位代表SpecMode(测量模式),低30位代表SpecSize(某种测量模式下的规格大小)
关于SpecMode 有三类;
UNSPECIFIED:
这种模式父容器不对它有任何限制,要多大给多大空间,这种情况一般应用于系统内部,表示一种测量状态。
EXACTLY:
父容器已检测出View所需要精确的大小,这个时候View的最终大小就是SpecSize,它对应LayoutParams的match_parent和具体的数值两种模式。
AT_MOST:
父容器指定了一个可用的大小即SpecSize,View的大小不能大于这个值,具体是什么值要看不同View的具体实现。它对应与LayoutParams的warp_content.
measure过程又分为:View的measure与ViewGroup的measure过程,详细内容将出现在 ##《View的工作流程》## 文章中—-
除了View的三大绘制流程,View的常见回调方法也需要掌握,比如构造方法onAttach, onVisibilityChanged,onDetach等。