内容简介:理解 View 的工作原理,熟悉自定义 View 的各种套路。本章节配套源码
理解 View 的工作原理,熟悉自定义 View 的各种套路。
1. ViewRoot 和 DecorView
- ViewRoot 对应于 ViewRootImpl 类,它是连接 WindowManager 和DecorView 的纽带。
- View 的绘制流程是从 ViewRoot 的
performtraversals
方法开始的:
- measure 的过程决定了 View 的宽高,完成后可以通过 getMeasuredWidth 和 getMeasuredHeight 获取。
- layout 的过程决定了 View 的四个顶点的坐标和实际宽高,完成后可以通过
getTop
、getBottom
、getLeft
和getRight
获取。 - draw 的过程决定了 View 的显示,完成后才能呈现在屏幕上。
- 在 Activity 中通过
setContentView
的布局可以通过以下方式获取到:ViewGroup content = findViewById(android.R.id.content);
2. MeasureSpec
- MeasureSpec 和父容器决定了 View 的尺寸规格。
-
MeasureSpec 是一个 32 位的 int 值,高 2 位代表 SpecMode ,低 30 位代表 SpecSize 。
> int mode = MeasureSpec.getMode(spec); > int size = MeasureSpec.getSize(spec); >
-
SpecMode 有三类:
- UNSPECIFIED :父容器不对 View 有任何限制,要多大给多大。
- EXACTLY :父容器已经检测出 View 所需的精确代销,这时 View 的最终大小就是 SpecSize 的值,它对应于 LayoutParams 中的 match_parent 或具体大小数值。
- AT_MOST :父容器指定了一个大小 SpecSize,View 不能大于这个值,最终大小取决于 View 的具体实现,它对应于 LayoutParams 中的 wrap_content。
- 我们在给 View 设置 LayoutParams 后,系统会在父容器的约束下将 LayoutParams 转换成对应的 MeasureSpec。
- 顶级 DecorView,其 MeasureSpec 由窗口尺寸和自身 LayoutParams 决定。
- 普通 View,其 MeasureSpec 由父容器的 MeasureSpec 和自身 LayoutParams 决定。
- 普通 View 的 MeasureSpec 创建规则:
childLayoutParams \ parentSpecMode | EXACTLY | AT_MOST | UNSPECIFIED |
---|---|---|---|
dp/px | EXACTLY childSize |
EXACTLY childSize |
EXACTLY childSize |
match_parent | EXACTLY parentSize |
AT_MOST parentSize |
UNSPECIFIED 0 |
wrap_content | AT_MOST parentSize |
AT_MOST parentSize |
UNSPECIFIED 0 |
3. View 的工作流程
3.1 measure 过程
- 分两种情况,如果是一个原始 View,通过
measure
方法就完成了测量过程;如果是 ViewGroup,除了完成自己的测量,还要遍历子元素的measure
方法。 - 直接继承 View 的自定义控件需要重写
onMeasure
方法并设置 wrap_content 时的自身大小,否则在布局中使用 wrap_content 时就相当于使用 match_parent; - ViewGroup 是一个抽象类,并没有定义其具体的测量过程,但它提供了一个
measureChildren(int widthMeasureSpec, int heightMeasureSpec)
的方法来测量子元素,该方法里循环调用measureChild(child, widthMeasureSpec, heightMeasureSpec)
方法,measureChild
的思想就是取出元素的 LayoutParams,然后通过getChildMeasureSpec
方法创建子元素的 MeasureSpec,最后将 MeasureSpec 传递给 View 的measure
方法来进行测量。 - 简化版的流程图如下:
- 由于在一些极端情况下,系统要在多次 measure 后才能确定测量的结果,所以在 onMeasure 中拿到的宽高可能不准确,推荐在 onLayout 中获取。
-
准确获取 View 宽高的四种方式:
- Activity/View#onWindowFocusChanged
- view.post(runnable)
-
ViewTreeObserver
... ViewTreeObserver observer = view.getViewTreeObserver(); observer.addOnGlobalLayoutListener ...
-
view.measure(int widthMeasureSpec, int heightMeasureSpec)
3.2 layout 过程
- View 的 onLayout 方法为空实现,而 ViewGroup 继承自 View 也没有实现该方法,所以 onLayout 具体实现由父容器的特性决定,比如 LinearLayout。
- 简易的流程图如下:
- 在 View 的默认实现中,View 的测量宽高和最终宽高是相等的,只不过测量宽高形成于 measure 过程,而最终宽高形成于 layout 过程,两者赋值时机不同。
3.3 draw 过程
- draw 的绘制过程遵循如下几步:
background.draw(canvas) onDraw dispatchDraw onDrawScrollBars
- ViewGroup 默认启用绘制优化标记位,如果 ViewGroup 需要通过 onDraw 来绘制内容,我们可以通过
setWillNotDraw(false);
来关闭该标记位。
4. 自定义 View
- 自定义 View 大体可以分为四类:
- 继承 View 重写 onDraw 方法
- 继承 ViewGroup 派生特殊的 Layout
- 继承特定的 View(比如 TextView)
- 继承特定的 ViewGroup(比如 LinearLayout)
- 自定义 View 须知:
post View#onDetachedFromWindow
本章节配套源码 戳我
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Java Concurrency in Practice
Brian Goetz、Tim Peierls、Joshua Bloch、Joseph Bowbeer、David Holmes、Doug Lea / Addison-Wesley Professional / 2006-5-19 / USD 59.99
This book covers: Basic concepts of concurrency and thread safety Techniques for building and composing thread-safe classes Using the concurrency building blocks in java.util.concurrent Pe......一起来看看 《Java Concurrency in Practice》 这本书的介绍吧!