文章目录
View基础
1.简介
-
View类是Android中各种组件的基类(如View是ViewGroup基类)。
-
View表现为显示在屏幕上的各种视图,Android中的UI组件都由View、ViewGroup组成。
-
它定义了所有UI组件共有的特性和行为(绘制、事件处理、状态变化……)。
2.分类
- 单一视图 :即一个View,(如TextView),不包含子View。
- 视图组:即多个View组成的ViewGroup(如LinearLayout) 包含子View。
3.视图结构
-
PhoneWindow是Android系统中最基本的窗口系统,继承自Windows类,负责管理界面显示以及事件响应。它是Activity与View系统交互的接口。
-
DecorView是PhoneWindow中的起始节点View,继承于View类,作为整个视图容器来使用。用于设置窗口属性。
-
ViewRoot在Activtiy启动时创建,负责管理、布局、渲染窗口UI等等。
- View的层级结构是树形的,遍历时遵循自上而下的顺序(树形递归)。
4.生命周期
测量(Measure):
- 系统会调用
onMeasure()
方法来确定View
的大小。- 这个方法通常会调用
getDefaultSize()
来获取布局文件中定义的宽度和高度,或者根据子视图来计算大小。布局(Layout):
- 系统会调用
onLayout()
方法来确定View
及其子视图在父视图中的位置。- 这个方法是在所有子视图的
onMeasure()
方法都被调用之后调用的。绘制(Draw):
- 系统会调用
onDraw()
方法来绘制View
的内容。- 这个方法通常会在视图的布局确定之后调用。
状态变化(State Change):
- 当
View
的状态发生变化时(例如,是否选中、是否禁用、是否有焦点等),会调用相应的回调方法,如onDrawableStateChanged()
。焦点变化(Focus Change):
- 当
View
获取或失去焦点时,会调用onFocusChange()
方法。触摸事件(Touch Event):
- 当用户与
View
交互时,会调用触摸事件相关的回调方法,如onTouchEvent()
、onKeyDown()
、onKeyUp()
等。布局变化(Layout Change):
- 当
View
的布局参数发生变化时,会调用onLayoutChange()
方法。视图回收(View Recycling):
- 当
View
不再可见时,系统可能会将其回收以重用。被回收的View
对象会被放入一个回收池中,等待未来重新使用。
5.绘制流程
View
类的performTraversals()
方法,它内部会调用一系列的回调方法来完成这些任务。测量(Measure):
- 系统会调用
View
的onMeasure()
方法来确定View
的大小。在这个阶段,View
会确定自己的宽度和高度,可以通过setMeasuredDimension()
方法来设置测量结果。布局(Layout):
- 在测量完成后,系统会调用
View
的onLayout()
方法,在这个方法中,View
会计算其在容器中的位置,即确定其左上角的位置。绘制(Draw):
- 系统会调用
View
的onDraw()
方法来绘制View
的内容。在这个阶段,View
可以调用Canvas
对象的绘图方法来绘制自己。这包括绘制文本、图像、形状等。
自定义View
1.分类
-
自定义View
-
在没有现成的View,需要自己实现的时候,就使用自定义View。
-
一般继承自View,SurfaceView或其他的View。
-
自定义View主要是实现
onMeasure + onDraw
。
-
-
自定义ViewGroup
- 自定义ViewGroup一般是利用现有的组件根据特定的布局方式来组成新的组件。
- 大多继承自ViewGroup或各种Layout。
- 自定义ViewGroup主要是实现
onMeasure + onLayout
。
2.基本方法
- View在Activity中显示出来,要经历测量、布局和绘制三个步骤,分别对应三个动作:measure、layout和draw。
- 测量:onMeasure()决定View的大小;
- 布局:onLayout()决定View在ViewGroup中的位置;
- 绘制:onDraw()决定绘制这个View。
3.自定义View的绘制流程
4.View的坐标与位置
-
Android的坐标系定义为:屏幕的左上角为坐标原点,向右为x轴增大方向,向下为y轴增大方向。
-
View的位置由4个顶点决定,4个顶点的位置描述分别由4个值决定:
Top:子View上边界到父view上边界的距离。
Left:子View左边界到父view左边界的距离。
Bottom:子View下边距到父View上边界的距离。
Right:子View右边界到父view左边界的距离。
MeasureSpec
1.简介
- 用来描述一个视图(View)或者布局(Layout)的测量规格。
MeasureSpec
包含了视图需要测量的大小限制信息,包括宽度和高度的限制。- 在视图层次结构中的每个节点在布局过程中都会经历两次测量过程:一次用于确定其宽度的测量规格,另一次用于确定其高度的测量规格。
- 测量规格由
MeasureSpec
类表示,它是一个int值,可以分解为两个部分:模式(Mode)和大小(Size)。 - MeasureSpec是View中的内部类,基本都是二进制运算。由于int是32位的,用高两位表示mode,低30位表示size,MODE_SHIFT = 30的作用是移位。
2.模式部分对子视图的限制
UNSPECIFIED
:父视图没有对子视图的尺寸做任何限制,子视图可以任意大小。EXACTLY
:父视图精确地确定了子视图的大小,子视图必须严格按照这个大小来测量。AT_MOST
:父视图对子视图的大小做了最大值的限制,子视图的大小不能超过这个限制,但可以小于这个限制。
3.LayoutParams与MeasureSpec的关系
-
在视图的测量阶段(Measure phase),视图会根据其
LayoutParams
以及父视图的MeasureSpec
来确定自己的尺寸。 -
MeasureSpec
提供了父视图对子视图尺寸的限制信息,而LayoutParams
则提供了子视图对自己尺寸的期望或要求。视图确定尺寸的过程
-
父视图通过
MeasureSpec
提供了对子视图尺寸的限制。 -
子视图根据自己的
LayoutParams
以及父视图的MeasureSpec
来确定自己的尺寸。 -
如果子视图是
ViewGroup
,它还需要对它的子视图递归地调用measure
方法来进行测量。
果一个
LinearLayout
的LayoutParams
设置了一个固定宽度,而父视图的MeasureSpec
是AT_MOST
模式,那么子视图的最终宽度将由其LayoutParams
中的宽度值和父视图的可用空间共同决定。 -
4.进行measure的意义
- 响应布局变化:当视图的布局参数发生变化时(例如,
LinearLayout
的LayoutParams
中宽度或高度的值发生变化),视图需要重新测量以反映新的尺寸。 - 适应父视图空间:视图需要根据父视图提供的空间(由
MeasureSpec
表示)来确定自己的大小。父视图可能限制了视图的最大尺寸,或者提供了视图可以使用的可用空间。 - 视图间的关系:在有约束的布局中(如
ConstraintLayout
),视图之间可能存在依赖关系,如Match_Parent
或Wrap_Content
。这些关系需要在测量阶段解决,以确保视图按预期分配空间。 - 内容自适应:对于内容驱动的视图(如
TextView
),其内容的大小可能会改变(例如,文本被剪切或滚动),这时视图需要重新测量以适应新的内容尺寸。 - 布局重排:当视图树的结构发生变化时(例如,新的视图被添加或删除),整个视图树可能需要重新测量和布局,以确保所有视图都正确放置和大小。
- 属性变化:视图的某些属性可能会影响其大小,例如
android:layout_margin
、android:padding
等。当这些属性发生变化时,视图需要重新测量。 - 屏幕方向变化:当设备屏幕方向变化时(例如,从 portrait 到 landscape),视图的尺寸可能会发生变化,因此需要重新测量。
- 属性变化:视图的某些属性可能会影响其大小,例如
android:layout_margin
、android:padding
等。当这些属性发生变化时,视图需要重新测量。 - 屏幕方向变化:当设备屏幕方向变化时(例如,从 portrait 到 landscape),视图的尺寸可能会发生变化,因此需要重新测量。