
我的 Android 知识体系,欢迎 Star https://github.com/daishengda2018/AndroidKnowledgeSystem
onMeasure、onLayout 可以说是自定 View 的核心,但是很多开发者都没能理解其含义与作用,也不理解 onMeasure 、 xml 指定大小这二者的关系与差异,也不能区分 getMeasureWidth 与 getWidth 的本质区别又是什么。本文将通过理论加实践的方法带领大家深入理解 onMeasure 、onLayout 的定义、流程、具体使用方法与需要注意的细节。
自定义View —— onMeasure、 onLayout
布局过程的作用
- 确定每个View的尺寸和位置
- 作用:为绘制和触摸范围做支持
- 绘制:知道往哪里了画
- 触摸返回:知道用户点的是哪里
布局的流程
从整体看
- 测量流程:从根 View 递归调用每一级子 View 的 measure 方法,对它们进行测量。
- 布局流程:从根 View 递归调用每一级子 View 的 layout 方法,把测量过程得出的子 View 的位置和尺寸传给子 View,子 View 保存。
从个体看
对于每一个 View:
- 运行前,开发者会根据自己的需求在 xml 文件中写下对于 View 大小的期望值
- 在运行的时候,父 View 会在
onMeaure()
中,根据开发者在 xml 中写的对子 View 的要求, 和自身的实际可用空间,得出对于子 View 的具体尺寸要求 - 子 View 在自己的
onMeasure
中,根据 xml 中指定的期望值和自身特点(指 View 的定义者在onMeasrue
中的声明)算出自己的*期望
如果是 ViewGroup 还会在
onMeasure
中,调用每个子 View 的 measure () 进行测量.
- 父 View 在子 View 计算出期望尺寸后,得出⼦ View 的实际尺⼨和位置
- ⼦ View 在自己的 layout() ⽅法中将父 View 传进来的实际尺寸和位置保存
如果是 ViewGroup,还会在 onLayout() ⾥调用每个字 View 的 layout() 把它们的尺寸 置传给它们
为啥需要两个过程呢?
原因一
measure 的测量过程可能不止一次,比如有三个子 View 在一个 ViewGroup 里面,ViewGroup 的宽度是 warp_content,A 的宽度是 match_parent, B 和 C 是 warp_content, 此时 ViewGroup 的宽度是不固定的,怎么确定 A 的 match_parent 到底有多大呢?此时是如何测量的呢?
以 LinearLayout 为例:第一次测量 LinearLayout 的大小也是没有确定的,所以无法确定 A 的 match_parent 到底有多大,这时候的 LinearLayout 会对 A 直接测量为 0 ,然后测量 B、C 的宽度,因为 B、C 的大小是包裹内容的,在测量后就可以确定 LinearLayout 的宽度了:即为最长的 B 的宽度。

这时候再对 A 进行第二次测量,直接设置为与 LinearLayout 相同的宽度,至此达到了 match_parent 的效果。

如果将 measure 和 layout 的过程糅合在一起,会导致两次测量的时候进行无用的 layout,消耗了更多的资源,所以为了性能,将其二者分开。
原因二
也是二者的职责相互独立,分为两个过程,可以使流程、代码更加清晰。
拓展
上面例子中的情况仅仅存在于 LinearLayout中,每种布局的测量机制是不同的。那么如果 A B C 三个 View 都是 match_parent LinearLayout 是如何做的呢?
- 第一轮测量:LinearLayout 无法确定自己的大小,所以遇到子 View match_parent 都会测量为 0

- 第二轮测量:都没有大小,LinearLayout 会让所有子 View 自由测量(父 View 不限制宽度)。每个测量之后都会变为和最宽的一样的宽度。

注意:
- onMeasure 与 measure() 、onDraw 与 draw 的区别
onXX 方法是调度过程,而 measure、draw 才是真正做事情的。可以从源码中看到 measure 中调用了 onMeasure 方法。
```java public final void measure(int widthMeasureSpec, int heightMeasureSpec) { // …………… if (cacheIndex < 0 || sIgnoreMeasureCache) { // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); // ……………… } }
```
- 为什么不把对于尺寸的要求直接交个子 View 而是要交给父 View 呢?
因为有些场景子 View 的大小需要父 View 进行规划,例如上面的例子中 LinearLayout 的子 View 设置了 weight。
- layout() 很少被使用到,因为他的改变没有通知父 View,这可能会导致布局重叠等问题 。在下面的「综合演练 —— 简单改写已有 View 的尺寸」中会有一个证明。
onMeasure 方法
要明确的一个问题是: 什么时候需要我们自己实现 onMeasure 方法呢?
答:具体开发的时候有以下三种场景:
- 当我们继承一个已有 View 的时候,简单改写他们的尺寸,比如自定义一个正方形的 ImageView,取宽高中较大的值为边长。
- 完全进行自定义尺寸的计算。比如实现一个绘制圆形的 View 我们需要在尺寸为 warp_content 时指定一个大小例如下文中的「综合演练 —— 完全自定义 View 的尺寸」。
- 自定义 Layout,这时候内部所有的子 View 的尺寸和位置都需要我们自己控制,需要重写
onMeasure()
和onLayout()
方法。例如下文中的「综合演练 —— 自定义 Layout」
onLayout 方法
onLayout 方法是 ViewGroup 中用于控制子 View 位置的方法。放置子 View 位置的过程很简单,只需重写 onLayout 方法,然后获取子 View 的实例,调用子 View 的 layout 方法实现布局。在实际开发中,一般要配合 onMeasure 测量方法一起使用。在下文「综合演练 —— 自定义 Layout」中会详细演示。
综合演练
简单改写已有 View 的尺寸实现方形 ImageView
- 首先来证明一下改写 layout 方法会存在的问题
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/** * 自定义正方形 ImageView * * Created by im_dsd on 2019-08-24 */ public class SquareImageView extends android.support.v7.widget.AppCompatImageView { public SquareImageView(Context context) { super(context); } public SquareImageView(Context context, AttributeSet attrs) { super(context, attrs); } public SquareImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public void layout(int l, int t, int r, int b) { // 使用宽高的最大值设置边长 int width = r - l; int height = b - t; int size = Math.max(width, height); super.layout(l, t, l + size, t + size); } }
代码很简单,获取宽与高的最大值用于设置正方形 View 的边长。再看一下布局文件的设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" tools:context=".MainActivity"> <com.example.dsd.demo.ui.custom.measure.SquareImageView android:background="@color/colorAccent" android:layout_width="200dp" android:layout_height="300dp"/> <View android:background="@android:color/holo_blue_bright" android:layout_width="200dp" android:layout_height="200dp"/> </LinearLayout>
通过布局文件的描述如果是普通的 View 显示的状态应该是这样的

而我们期待的状态应该是这样的:SquareImageView 的宽高均为 300dp。

但是最终的结果却是下图,虽然我们使用了 LinearLayout 但是我们通过layout()
方法改变了 SquareImageView 的大小,对于这个变化LinearLayout 并不知道,所以会发生布局重叠的问题。可见一般情况下不要使用 layout()
方法。

- 通过
onMeasure
方法更改尺寸。
1
2
3
4
5
6
7
8
9
10
11@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // super.onMeasure 中已经完成了 View 的测量 super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 获取测量的结果比较后得出最大值 int height = getMeasuredHeight(); int width = getMeasuredWidth(); int size = Math.max(width, height); // 将结果设置回去 setMeasuredDimension(size, size); }
总结
简单来说,更改已有 View 的尺寸主要分为以下步骤
- 重写
onMeasure()
- 用
getMeasureWidth
和getMeasureHeight()
获取测量尺寸 - 计算最终要的尺寸
- 用
setMeasuredDimension(width, height)
把结果保存
完全自定义 View 的尺寸
此处用绘制圆形的 CircleView 做一个例子。对于这个 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
24
25
26
27
28
29
30
31
32
33
34
35/** * 自定义 View 简单测量 * Created by im_dsd on 2019-08-15 */ public class CircleView extends View { private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); /** * 为了方便简单,固定尺寸 */ private static final float PADDING = DisplayUtils.dp2px(20); private static final float RADIUS = DisplayUtils.dp2px(80); public CircleView(Context context) { super(context); } public CircleView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.RED); canvas.drawCircle(PADDING + RADIUS, PADDING + RADIUS, RADIUS, mPaint); } } <com.example.dsd.demo.ui.custom.layout.CircleView android:background="@android:color/background_dark" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
此时将大小设置为 wrap_content 包裹布局,结果会是怎么样的呢?

竟然填充了屏幕!根本就没有包裹内容,此时就需要我们大展身手了
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@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 没有必要再让 view 自己测量一遍了,浪费资源 // super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 计算期望的 size int size = (int) ((PADDING + RADIUS) * 2); // 获取父 View 传递来的可用大小 int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); // 开始计算 int result = 0; switch (widthMode) { // 不超过 case MeasureSpec.AT_MOST: // 在 AT_MOST 模式下,取二者的最小值 if (widthSize < size) { result = widthSize; } else { result = size; } break; // 精准的 case MeasureSpec.EXACTLY: // 父 View 给多少用多少 result = widthSize; break; // 无限大,没有指定大小 case MeasureSpec.UNSPECIFIED: // 使用计算出的大小 result = size; break; default: result = 0; break; } // 设置大小 setMeasuredDimension(result, result); }

上面的代码就是 onMeasure(int,int)
的模板代码了,要注意一点的是需要注释 super.onMeasure
方法,此处面试的时候普遍会问。
1
2// 没有必要再让 view 自己测量一遍了,浪费资源 // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
这段模版代码其实 Android SDK 里面早就有了很好的封装 : resolveSize(int size, int measureSpec)
和 resolveSizeAndState(int size, int measureSpec, int childMeasuredState)
,两行代码直接搞定。
1
2
3
4
5
6
7
8
9
10
11
12
13@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 没有必要再让 view 自己测量一遍了,浪费资源 // super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 计算期望的 size int size = (int) ((PADDING + RADIUS) * 2); // 指定期望的 size int width = resolveSize(size, widthMeasureSpec); int height = resolveSize(size, heightMeasureSpec); // 设置大小 setMeasuredDimension(width, height); }
使用的时候完全可以这样做,但是非常建议大家都自己手写几遍理解其中的含义,因为面试会问到其中的细节。
还有一点很遗憾,就是 resolveSizeAndState(int, int, int)
不好用。不好用的原因不是方法有问题,而是很多自定义 View 包括原生的 View 都没有使用 resolveSizeAndState(int, int, int)
方法,或者没用指定 sate (state 传递父 View 对于子 View 的期望,相比resolveSize(int, in)
方法对于子 View 的控制更好)所以就算设置了,也不会起作用。
总结
完全自定义 View 的尺寸主要分为以下步骤:
- 重写
onMeasure()
- 计算自己期望的尺寸
- 用
resolveSize()
或者resolveSizeAndState()
修正结果 - 用
setMeasuredDimension(width, height)
保存结果
自定义 Layout
源码地址
以 TagLayout 为例一步一步实现一个自定义 Layout。具体期望的效果如下图:

重写 onLayout()
在继承 ViewGroup 的时候 onLayout()
是必须要实现的,这意味着子 View 的位置摆放的规则,全部交由开发者定义。
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/** * 自定义 Layout Demo * * Created by im_dsd on 2019-08-11 */ public class TagLayout extends ViewGroup { public TagLayout(Context context) { super(context); } public TagLayout(Context context, AttributeSet attrs) { super(context, attrs); } public TagLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); // 此时所有的子 View 都和 TagLayout 一样大 child.layout(l, t, r, b); } } }
实验一下是否和期望的效果一样呢
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<?xml version="1.0" encoding="utf-8"?> <com.example.dsd.demo.ui.custom.layout.TagLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dp" android:padding="5dp" android:background="#ffee00" android:textSize="16sp" android:textStyle="bold" android:text="音乐" /> </com.example.dsd.demo.ui.custom.layout.TagLayout>

的确和期望一致。如果想要 TextView 显示为 TagLayout 的四分之一呢?
1
2
3
4
5
6
7
8@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); // 子 View 显示为 TagLayout 的 1/4 child.layout(l, t, r / 4, b / 4); } }
效果达成!!!很明显onLayout
可以非常灵活的控制 View 的位置

再尝试让两个 View 呈对角线布局呢?
1
2
3
4
5
6
7
8
9
10
11@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (i == 0 ){ child.layout(0, 0, (r - l) / 2, (b - t) / 2); } else { child.layout((r - l) / 2, (b - t) / 2, (r - l), (b - t)); } } }

onLayout
的方法还是很简单的,但是在真正布局中怎么获取 View 的位置才是难点!如何获取呢,这时候就需要 onMeasure
的帮助了!
计算
在写具体的代码之前,先来搭建大体的框架。主要的思路就是在 onMeasure()
方法中计算好子 View 的尺寸和位置信息包括 TagLayout 的具体尺寸,然后在onLayout()
方法中摆放子 View。
在计算过程中涉及到三个难点,具体请看注释
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
35private List<Rect> mChildRectList = new ArrayList<>(); @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 没有必要让 View 自己算了,浪费资源。 // super.onMeasure(widthMeasureSpec, heightMeasureSpec); for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); // 难点1: 计算出对于每个子 View 的尺寸 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); // 难点2:计算出每一个子 View 的位置并保存。 Rect rect = new Rect(?, ?, ?, ?); mChildRectList.add(rect); } // 难点3:根据所有子 View 的尺寸计算出 TagLayout 的尺寸 int measureWidth = ?; int measureHeight = ?; setMeasuredDimension(measureWidth, measureHeight); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (mChildRectList.size() == 0) { return; } for (int i = 0; i < getChildCount(); i++) { if (mChildRectList.size() <= i) { return; } View child = getChildAt(i); // 通过保存好的位置,设置子 View Rect rect = mChildRectList.get(i); child.layout(rect.left, rect.top, rect.right, rect.bottom); } }
难点1 :如何计算子 View 的尺寸。
主要涉及两点:开发者对于子 View 的尺寸设置和父 View 的具体可用空间。获取开发者对于子 View 尺寸的设置就比较简单了:
1
2
3
4// 获取开发者对于子 View 尺寸的设置 LayoutParams layoutParams = child.getLayoutParams(); int width = layoutParams.width; int height = layoutParams.height;
获取父 View (TagLayout) 的可用空间要结合两点:
- TagLayout 的父 View 对于他的尺寸限制
- TagLayout 的剩余空间。我们用 width 为例用伪代码简单分析一下如何计算子 View 的尺寸
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
39int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); // TagLayout 已经使用过的空间,此处的计算是个难点,此处不是本例子重点,一会儿讨论 int widthUseSize = 0; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); // 获取开发者对于子 View 尺寸的设置 LayoutParams layoutParams = child.getLayoutParams(); int childWidthMode; int childWidthSize; // 获取父 View 具体的可用空间 switch (layoutParams.width) { // 如果说子 View 被开发者设置为 match_parent case LayoutParams.MATCH_PARENT: switch (widthMode) { case MeasureSpec.EXACTLY: // TagLayout 为 EXACTLY 模式下,子 View 可以填充的部位就是 TagLayout 的可用空间 case MeasureSpec.AT_MOST: // TagLayout 为 AT_MOST 模式下有一个最大可用空间,子 View 要是想 match_parent 其实是和 // EXACTLY 模式一样的 childWidthMode = MeasureSpec.EXACTLY; childWidthSize = widthSize - widthUseSize; break; case MeasureSpec.UNSPECIFIED: // 当 TagLayout 为 UNSPECIFIED 不限制尺寸的时候,意味着可用空间无限大!空间无限大还想 // match_parent 二者完全是悖论,所以我们也要将子 View 的 mode 指定为 UNSPECIFIED childWidthMode = MeasureSpec.UNSPECIFIED; // 此时 size 已经没有作用了,写 0 就可以了 childWidthSize = 0; break; } case LayoutParams.WRAP_CONTENT: break; default: // 具体设置的尺寸 break; } // 获取 measureSpec int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, childWidthMode);
补充一下什么时候会是 UNSPECIFIED 模式呢?比如说横向或纵向滑动的 ScrollView,他的宽度或者高度的模式就是 UNSPECIFIED
伪代码仅仅模拟了开发者将子 View 的 size 设置为 match_parent 的情况,其他的情况读者要是感兴趣可以自己分析一下。笔者就不做过多的分析了!因为 Android SDK 早就为我们提供好了可用的 API: measureChildWithMargins(int, int, int, int)
一句话就完成了对于子 View 的测量。
难点2:计算出每一个子 View 的位置并保存。
难点3:根据所有子 View 的尺寸计算出 TagLayout 的尺寸
有了 measureChildWithMargins
方法,对于子 View 的测量就很简单啦。 一口气解决难点 2 3。
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@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int lineHeightUsed = 0; int lineWidthUsed = 0; int widthUsed = 0; int heightUsed = 0; int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); // 测量子 View 尺寸。TagLayout 的子 view 是可以换行的,所以设置 widthUsed 参数为 0 // 让子 View 的尺寸不会受到挤压。 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUsed); if (widthMode != MeasureSpec.UNSPECIFIED && lineWidthUsed + child.getMeasuredWidth() > widthSize) { // 需要换行了 lineWidthUsed = 0; heightUsed += lineHeightUsed; measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUsed); } Rect childBound; if (mChildRectList.size() >= i) { // 不存在则创建 childBound = new Rect(); mChildRectList.add(childBound); } else { childBound = mChildRectList.get(i); } // 存储 child 位置信息 childBound.set(lineWidthUsed, heightUsed, lineWidthUsed + child.getMeasuredWidth(), heightUsed + child.getMeasuredHeight()); // 更新位置信息 lineWidthUsed += child.getMeasuredWidth(); // 获取一行中最大的尺寸 lineHeightUsed = Math.max(lineHeightUsed, child.getMeasuredHeight()); widthUsed = Math.max(lineWidthUsed, widthUsed); } // 使用的宽度和高度就是 TagLayout 的宽高啦 heightUsed += lineHeightUsed; setMeasuredDimension(widthUsed, heightUsed); }
终于写完代码啦,运行起来瞧瞧看。

竟然奔溃了!通过日志可以定位到是
1
2
3// 对于子 View 的测量 measureChildWithMargins(child, widthMeasureSpec, widthUsed, heightMeasureSpec, heightUsed);
这一句出了问题,通过源码得知measureChildWithMargins
方法会有一个类型转换导致了崩溃
1
2
3
4protected void measureChildWithMargins(int, int ,int, int) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); ……………… }
解决办法就是在 TagLayout 中重写方法 generateLayoutParams(AttributeSet)
返回一个 MarginLayoutParams 就可以解决问题了。
1
2
3
4@Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); }
再次运行达到最终目标!

总结
自定义 Layout 的主要步骤分为以下几点:
- 重写
onMeasure()
- 遍历每一个子 View,用
measureChildWidthMargins()
测量 View
- MarginLayoutParams 和 generateLayoutParams()
- 有些子 View 可能需要多次测量
- 测量完成后,得出子 View 的实际尺寸和位置,并暂时保存
- 测量出所有子 View 的位置和尺寸后,计算出自己的尺寸,并用
setMeasuredDimension(width, height)
保存 - 重写
onLayout()
- 遍历每个子 View,调用它们的 layout() 方法来将位置和尺寸传递给它们。
getMeasureWidth 与 getWidth 的区别
getMeasureXX 代表的是 onMeasure 方法结束后(准确的说应该是测量结束后)测量的值,而 getXX 代表的是 layout 阶段 right - left、bottom - top 的真实显示值,所以第一个不同点就是赋值的阶段不同,可见 getXXX 在 layout() 之前一直为 0, 而 getMeasureXX 可能不是最终值( onMeasure 可能会被调用多次),但是最终的时候二者的数值都会是相同的。使用那个还需要看具体的场景。
总结: getMeasureXX 获取的是临时的值,而 getXX 获取的时候最终定稿的值,一般在绘制阶段、触摸反馈阶段使用 getXXX,在 onMeasure 阶段被迫使用 getMeasureXX 。
本文所有源码地址
我的 Android 知识体系,欢迎 Star https://github.com/daishengda2018/AndroidKnowledgeSystem
最后
以上就是忐忑美女最近收集整理的关于android 自定义view如何控制view的高度_[Android 自定义 View] —— 深入总结 onMeasure、 onLayout的全部内容,更多相关android内容请搜索靠谱客的其他文章。
发表评论 取消回复