ViewRoot,DecorView,MeasureSpec和View的工作原理——Android开发艺术探索笔记
来源:互联网 发布:vivo深圳研发待遇知乎 编辑:程序博客网 时间:2024/04/30 15:30
原文链接 http://sparkyuan.me/ 转载请注明出处
View的绘制流程是从ViewRoot的performTraversals方法开始的,它经过measure、layout和draw三个过程才能最终将一个View绘制出来,其中measure用来测量View的宽和高,layout用来确定View在父容器中的放置位置,而draw则负责将View绘制在屏幕上。
ViewRoot和DecorView
ViewRoot
- ViewRoot对应ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程均通过ViewRoot来完成。
- ActivityThread中,Activity创建完成后,会将DecorView添加到Window中,同时创建ViewRootImpl对象,并建立两者的关联。
DecorView
- DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下两个部分(具体情况和Android版本及主体有关),上面的是标题栏,下面的是内容栏。在Activity中通过setContentView所设置的布局文件其实就是被加到内容栏之中的,而内容栏的id是content,在代码中可以通过ViewGroup content = (ViewGroup)findViewById(R.android.id.content)来得到content对应的layout。
- DecorView其实是一个FrameLayout,View层的事件都先经过DecorView,然后才传递给我们的View。
MeasureSpec
在测量过程中,系统会将View的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec,然后再根据这个MeasureSpec来测量出View的宽和高。测量出来的宽和高不一定等于View最终的宽和高。
MeasureSpec将SpecMode和SpecSize打包成一个int值来避免过多的对象内存分配,高2位代表SpecMode,低30位代表SpecSize,SpecMode是指测量模式,而SpecSize是指在某种测量模式下的规格大小。
SpecMode有三类:
- UNSPECIFIED:父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量状态
- EXACTLY:父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值。它对应于LayoutParams中的match_parent和具体的数值这两种模式
- AT_MOST:父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体是什么值要看不同View的具体实现。它对应于LayoutParams中的wrap_content。
普通MeasureSpec的创建规则
对于普通View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定。
- 子View为精确宽高,无论父容器的MeasureSpec,子View的MeasureSpec都为精确值且遵循LayoutParams中的值。
- 子View为match_parent时,如果父容器是精确模式,则子View也为精确模式且为父容器的剩余空间大小;如果父容器是最大模式,则子View也是最大模式且不会超过父容器的剩余空间。
- 子View为wrap_content时,无论父View是精确还是最大模式,子View的模式总是最大模式,且不会超过父容器的剩余空间。
View的工作流程
measure
ViewGroup的measure方法会遍历每个子元素,并调用子元素内部的measure方法,measure源码如下:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
注:
- getDefaultSize()返回MeasureSpec中的specSize,也就是View测量后的大小。
- getSuggestedMinimumWidth(),View如果没有背景,那么返回android:minWidth这个属性指定的值,这个值可以为0;如果设置了背景,则返回背景的最小宽度和minWidth中的最大值。
- getSuggestedMinimumHeight(),与getSuggestedMinimumWidth()类似。
- 直接继承View的自定义控件需要重写onMeasure方法并设置wrap_content时的自身大小,否则在布局中使用wrap_content时就相当于使用match_parent。因为LayoutParams=wrap_content的情况下,MeasureSpec为AT_MOST,所以View的宽和高为父容器当前剩余的空间,这种效果与match_parent一致。具体处理方法要根据需求灵活决定。
如何得到View的宽和高
在Activity的onCreate、onStart、onResume方法中均无法正确得到某个View的宽/高信息,这是因为View的measure过程和Activity的生命周期方法不是同步执行的,因此无法保证Activity执行了onCreate、onStart、onResume时某个View就已经测量完毕了,如果View还没有测量完毕,那么获得的宽/高就是0。
可以通过如下四个方法来解决这个问题:
- Activity或者View的onWindowFocusChanged方法(注意该方法会在Activity Pause和resume时被多次调用)
- view.post(new Runnable( {@Overidde public void run(){})}),在run方法中获取。
- ViewTreeObserver中的onGlobalLayoutListener中。
- 手动调用View的measure方法。
示例代码请参考原书P190页
layout
layout的作用是用来确定子视图在父视图中的位置。源码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
通过setFrame()确定四个顶点的位置,进而确定View在父容器中的位置。
在View的默认实现中,View的测量宽/高和最终宽/高是相等的,只不过测量宽/高形成于View的measure过程,而最终宽/高形成于View的layout过程,即两者的赋值时机不同,测量宽/高的赋值时机稍微早一些。多数情况下可以认为View的测量宽/高就等于最终的宽/高,但对于在View的layout中改变了View的left、top、right、bottom四个属性时,得出的测量宽/高有可能和最终的宽/高不一致。
draw
draw的过程很简单主要有以下几步:
- 绘制背景(background.draw)
- 绘制自己(onDraw)
- 绘制children(dispatchDraw)
- 绘制装饰(onDrawScrollBars)。
源码如下
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
注:
- View有一个特殊的方法setWillNotDraw,如果一个View不需要绘制任何内容,设置这个标记位true后,系统会进行优化。默认情况下,View没有启用这个优化标记位,但是ViewGroup会默认启用这个优化标记位。
- 这个标记位对实际开发的意义是:如果自定义控件继承于ViewGroup并且本身不具备绘制功能时,就可以开启这个标记位从而便于系统进行后续的优化。当明确知道一个ViewGroup需要通过onDraw来绘制内容时,需要显示地关闭WILL_NOT_DRAW这个标记位。
欢迎转载,转载请注明本文链接http://blog.csdn.net/l664675249/article/details/50774617
- ViewRoot,DecorView,MeasureSpec和View的工作原理——Android开发艺术探索笔记
- ViewRoot,DecorView,MeasureSpec和View的工作原理——Android开发艺术探索笔记
- 【View工作原理】ViewRoot、DecorView、MeasureSpec和LayoutParams
- View的工作原理(一)——从ViewRoot和DecorView说起
- Android开发艺术探索学习笔记4——View的工作原理
- 《Android开发艺术探索》之View的事件体系和工作原理学习笔记
- android view的讲解 之 View的工作原理(decorView,viewRoot讲解)(一)
- Android开发艺术探索——第四章View的工作原理
- Android艺术开发探索第四章——View的工作原理(上)
- Android艺术开发探索第四章——View的工作原理(下)
- Android艺术开发探索第四章——View的工作原理(上)
- Android艺术开发探索第四章——View的工作原理(下)
- View的工作原理(Android开发艺术探索读书笔记)
- 开发艺术探索--View的工作原理
- View的工件原理:认识ViewRoot和DecorView
- 《Android开发艺术探索》之学习笔记(四)View的工作原理
- Android 开发艺术探索笔记 第四章 View的工作原理
- 《android开发艺术探索笔记》Part 4、View的工作原理
- 5-37 整数分解为若干项之和
- 关于安装orcale数据库中遇到的错误以及解决思路
- android有序广播和无序广播的区别
- threejs官网文档
- jQuery快速高效制作网页交互特效第五章课后5
- ViewRoot,DecorView,MeasureSpec和View的工作原理——Android开发艺术探索笔记
- java list中删除元素用remove()报错的fail-fast机制原理以及解决方案
- 人民币数字大小写转换
- 携程开源Redis多数据中心解决方案XPipe
- l1范数最小化求解系数方程_正交匹配追踪法(orthogonal matching pursuit)
- Saltstack实现Openstack管理
- ubuntu16.04安装caffe2(CPU only)
- Android ViewPager滑滑的~
- SGML XML HTML XHTML粗浅介绍