正文
前言
-
自定义
View
是
Android
开发者必须了解的基础
-
网上有大量关于自定义
View
原理的文章,但存在一些问题:
内容不全、思路不清晰、无源码分析、简单问题复杂化等等
-
今天,我将全面总结自定义
View
的全工作流程,我能保证这是
市面上的最全面、最清晰、最易懂的
-
本文秉着“结论先行、详细分析在后”的原则,即先让大家感性认识,再通过理性分析从而理解问题;
-
所以,请各位读者先记住结论,再往下继续看分析;
-
文章较长,阅读需要较长时间,建议收藏等充足时间再进行阅读
目录
1. 储备知识
1.1 ViewRoot
// 在主线程中,Activity对象被创建后:
// 1. 自动将DecorView添加到Window中 & 创建ViewRootImpll对象
root = new ViewRootImpl(view.getContent(),display);
// 3. 将ViewRootImpll对象与DecorView建立关联
root.setView(view,wparams,panelParentView)
1.2 DecorView
即
Android
视图树的根节点;同时也是
FrameLayout
的子类
View
层的事件都先经过
DecorView
,再传递到
View
-
特别说明
内含1个竖直方向的
LinearLayout
,分为2部分:上 = 标题栏
(titlebar)
、下 = 内容栏
(content)
在
Activity
中通过
setContentView()
所设置的布局文件其实是被加到内容栏之中的,成为其唯一子View = id为content的
FrameLayout
中
// 在代码中可通过content得到对应加载的布局
// 1. 得到content
ViewGroup content = (ViewGroup)findViewById(android.R.id.content);
// 2. 得到设置的View
ViewGroup rootView = (ViewGroup) content.getChildAt(0);
1.3 Window、Activity、DecorView 与 ViewRoot的关系
1.4 自定义View基础
了解自定义View流程前,需了解一定的自定义View基础,具体请看文章:
(1)自定义View基础 - 最易懂的自定义View原理系列
2. 绘制准备
故,下面我会先将绘制前的准备,再开始讲绘制流程
3. 绘制流程概述
-
从上可知,
View
的绘制流程开始于:
ViewRootImpl
对象的
performTraversals()
-
源码分析
/**
* 源码分析:ViewRootImpl.performTraversals()
*/
private void performTraversals() {
// 1. 执行measure流程
// 内部会调用performMeasure()
measureHierarchy(host, lp, res,desiredWindowWidth, desiredWindowHeight);
// 2. 执行layout流程
performLayout(lp, mWidth, mHeight);
// 3. 执行draw流程
performDraw();
}
-
从上面的
performTraversals()
可知:
View
的绘制流程从顶级
View(DecorView)
的
ViewGroup
开始,一层一层从
ViewGroup
至子
View
遍历测绘
即:自上而下遍历、由父视图到子视图、每一个
ViewGroup
负责测绘它所有的子视图,而最底层的 View 会负责测绘自身
-
绘制的流程 =
measure
过程、
layout
过程、
draw
过程,具体如下
下面,我将详细讲解
View
绘制的三大流程:
measure
过程、
layout
过程、
draw
过程
4. 详细介绍
4.1 Measure 过程
-
在某些情况下,需要多次测量
(measure)
才能确定
View
最终的宽/高;
-
该情况下,
measure
过程后得到的宽 / 高可能不准确;
-
此处建议:在
layout
过程中
onLayout()
去获取最终的宽 / 高
4.2 Layout过程
即计算
View
的四个顶点位置:
Left
、
Top
、
Right
和
Bottom
-
具体流程
请看文章:
自定义View Layout过程 - 最易懂的自定义View原理系列(3)