Android中View测量、布局及绘制原理
来源:互联网 发布:网络海豚壁纸图片 编辑:程序博客网 时间:2024/05/15 23:13
一、View绘制的流程框架
View的绘制是从上往下一层层迭代下来的。DecorView–>ViewGroup(—>ViewGroup)–>View ,按照这个流程从上往下,依次measure(测量),layout(布局),draw(绘制)。
二、Measure流程
顾名思义,就是测量每个控件的大小。
调用measure()方法,进行一些逻辑处理,然后调用onMeasure()方法,在其中调用
setMeasuredDimension()设定View的宽高信息,完成View的测量操作。
- 1
- 2
- 3
measure()方法中,传入了两个参数 widthMeasureSpec, heightMeasureSpec 表示View的宽高的一些信息。
- 1
- 2
- 3
- 4
由上述流程来看Measure流程很简单,关键点是在于widthMeasureSpec,heightMeasureSpec这两个参数信息怎么获得?
如果有了widthMeasureSpec, heightMeasureSpec,通过一定的处理(可以重写,自定义处理步骤),从中获取View的宽/高,调用setMeasuredDimension()方法,指定View的宽高,完成测量工作。
MeasureSpec的确定
先介绍下什么是MeasureSpec?
MeasureSpec由两部分组成,一部分是测量模式,另一部分是测量的尺寸大小。
其中,Mode模式共分为三类
UNSPECIFIED :不对View进行任何限制,要多大给多大,一般用于系统内部
EXACTLY:对应LayoutParams中的match_parent和具体数值这两种模式。检测到View所需要的精确大小,这时候View的最终大小就是SpecSize所指定的值
AT_MOST :对应LayoutParams中的wrap_content。View的大小不能大于父容器的大小。
子View的MeasureSpec值是根据子View的布局参数(LayoutParams)和父容器的MeasureSpec值计算得来的,具体计算逻辑封装在getChildMeasureSpec()里。
我们来看getChildMeasureSpec()的源码分析:
- 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
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
关于getChildMeasureSpec()里对于子View的测量模式和大小的判断逻辑有点复杂;
现总结如下表:
规律总结:(以子View为标准,横向观察)
当子View采用具体数值(dp / px)时,无论父容器的测量模式是什么,子View的测量模式都是EXACTLY且大小等于设置的具体数值;
当子View采用match_parent时,子View的测量模式与父容器的测量模式一致,若测量模式为EXACTLY,则子View的大小为父容器的剩余空间;若测量模式为AT_MOST,则子View的大小不超过父容器的剩余空间
当子View采用wrap_parent时,无论父容器的测量模式是什么,子View的测量模式都是AT_MOST且大小不超过父容器的剩余空间。
对于DecorView,其确定是通过屏幕的大小,和自身的布局参数LayoutParams。这部分很简单,根据LayoutParams的布局格式(match_parent,wrap_content或指定大小),将自身大小,和屏幕大小相比,设置一个不超过屏幕大小的宽高,以及对应模式。
从这里看出MeasureSpec的指定也是从顶层布局开始一层层往下去,父布局影响子布局。
关于MeasureSpec和View的Measure过程还可以看这篇文章
三、Layout流程
测量完View大小后,就需要将View布局在Window中,View的布局主要通过确定上下左右四个点来确定的。
其中布局也是自上而下,不同的是ViewGroup先在layout()中确定自己的布局,然后在onLayout()方法中再调用子View的layout()方法,让子View布局。在Measure过程中,ViewGroup一般是先测量子View的大小,然后再确定自身的大小。
layout()作用:确定View本身的位置,即设置View本身的四个顶点位置
源码分析如下:(仅贴出关键代码)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
上面看出通过 setFrame() / setOpticalFrame():确定View自身的位置,通过onLayout()确定子View的布局。 setOpticalFrame()内部也是调用了setFrame(),所以具体看setFrame()怎么确定自身的位置布局。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
确定了自身的位置后,就要通过onLayout()确定子View的布局。onLayout()是一个可继承的空方法。
- 1
- 2
- 3
如果当前View就是一个单一的View,那么没有子View,就不需要实现该方法。如果当前View是一个ViewGroup,就需要实现onLayout方法,该方法的实现个自定义ViewGroup时其特性有关,必须自己实现。
由此便完成了一层层的的布局工作。
四、Draw过程
View的绘制过程遵循如下几步:
①绘制背景 background.draw(canvas)
②绘制自己(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
无论是ViewGroup还是单一的View,都需要实现这套流程,不同的是,在ViewGroup中,实现了 dispatchDraw()方法,而在单一子View中不需要实现该方法。自定义View一般要重写onDraw()方法,在其中绘制不同的样式。
dispatchDraw()作用遍历子View并绘制
- Android中View测量、布局及绘制原理
- Android中View测量、布局及绘制原理
- 图解View测量、布局及绘制原理
- 深入了解View(四)—— LayoutInflater原理及View测量/布局/绘制总结
- view的测量,布局,绘制
- Android控件架构及View、ViewGroup的测量和绘制
- view的流程(测量,布局,绘制)
- View、ViewGroup的测量、布局、绘制流程
- android 中View测量,布局和滑动的属性区分
- 自定义控件(一):View的测量及绘制流程、原理简介
- Android群英传读书笔记---View测量和绘制
- 【Android自定义View】测量和绘制浅析
- Android View 绘制流程之测量(一)
- Android自定义View之绘制、测量
- Android UI测量、布局、绘制过程探究
- android View 绘制原理
- android view绘制原理
- 自定义View:测量measure,布局layout,绘制draw
- oracle的低阶操作(不定时更新)
- MFC-UpdateData(false)和UpdateData(true)的区别
- CefSharp 集成谷歌浏览器详解(四)--官网示例解析3 RegisterJsObject CefSharpSchemeHandlerFactory
- 《剑指offer》刷题笔记(知识迁移能力):数字在排序数组中出现的次数
- 手动安装m4, autoconf, automake, libtool
- Android中View测量、布局及绘制原理
- Codeforces Round #450 (Div. 2) D. Unusual Sequences 莫比乌斯系数容斥
- git统计操作
- 编译android源码学习的First Step
- Plugin execution not covered by lifecycle configuration
- Java的三种代理模式
- TensorFlow实现深度学习算法的教程汇集:代码+笔记
- java的会话管理:Cookie和Session
- 神经网络中的激活函数具体是什么?为什么ReLu要好过于tanh和sigmoid function?