Android View总结
Android 控件大致分两类:
ViewGroup当容器管 View,View自己负责绘制。整棵界面像 DOM 树:父视图负责测量、定位、绘制。findViewById之所以贵,是因为它从上到下遍历整棵控件树找实例,重复操作里尽量少用。
一、关于 Android View 控件
现在常用控件大多直接或间接继承自 View。ViewGroup 作为容器管理子 View,父视图负责测量、定位和绘制。
二、UI 界面架构
每个 Activity 含一个 PhoneWindow,它把 DecorView 设成窗口根视图。里面是熟悉的 TitleView 和 ContentView。平时的 setContentView() 设的就是 ContentView。
三、Android 如何绘制 View
Activity 启动时会被要求画出布局。框架处理这个请求的前提是布局合理。绘制从根视图开始,从上到下遍历:ViewGroup 负责让子 View 被绘制,每个 View 通过 draw() 画自己。三步走:
-
Measure -
Layout -
Draw
整条流程在 ViewRoot 的 performTraversals() 里展开:
画之前先知道尺寸和位置,所以先 measure、再 layout。
四、Measure 过程
计算实际大小,写入 mMeasuredHeight / mMeasuredWidth。MeasureSpec 是 32 位 int:高 2 位是模式,低 30 位是大小。三种模式:
-
EXACTLY:精确值。layout_width/height 为具体数值或 match_parent 时使用 -
AT_MOST:最大值。wrap_content 时不能超过父控件允许的最大尺寸 -
UNSPECIFIED:不指定,View 想多大就多大,一般很少用
measure 不可重写,自定义时重写 onMeasure:
最终宽高由 setMeasuredDimension() 设定;不重写则默认走 getDefaultSize。getMeasuredWidth() / getMeasuredHeight() 必须在 onMeasure 之后调用才有效。
五、Layout 过程
知道大小之后还要知道位置才能画。mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
四个参数是相对父视图的左、上、右、下,用的就是刚测出来的宽高。
setFrame 写入坐标。坐标变了就重新定位。View 的 onLayout 是空方法,定位由 ViewGroup 完成。layout 结束后 getWidth() / getHeight() 才返回正确值。
getWidth/Height() 和 getMeasuredWidth/Height() 的区别:
-
getWidth():布局设定完成后整个 View 的宽度 -
getMeasuredWidth():对内容测量后,内容占据的宽度
| API | 何时有效 | 含义 |
|---|---|---|
| getMeasuredWidth/Height | onMeasure 之后 | 测量阶段得到的内容尺寸 |
| getWidth/Height | layout 之后 | 布局完成后 View 的实际宽高 |
六、Draw 过程
重点是第 3 步 onDraw。其余是背景、fade、scrollbar。dispatchDraw 递归画子 View;没有子 View 就不需要。onDraw 要自己实现,每个控件画的内容不同,主要用 Canvas。
七、参考资料
-
Android视图绘制流程完全解析,带你一步步深入了解View(二) -
Android应用层View绘制流程与源码分析 -
How Android Draws Views -
What is the difference between getWidth/Height() and getMeasuredWidth/Height() in Android SDK?
原文出处:http://threezj.com/2015/12/17/Android%20View%E8%AF%A6%E8%A7%A3/
一句话总结:绘制从根向下走 Measure → Layout → Draw;自定义改 onMeasure/onLayout/onDraw,宽高 API 要分清测量值和布局值。
转载请注明来源:Android View总结








