Get into the habit of writing together! This is the 9th day of my participation in the "Nuggets Daily New Plan · April Update Challenge", click to view the details of the event .
In a custom View, if the width or height of the View is set to wrap_content and we do not do any processing, the final effect is the same as match_parent. This article mainly explains why this happens from the perspective of source code
1. If custom View is not processedwrap_content
Just customize a View:
class CustomView @JvmOverloads constructor(
context: Context,
attributes: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attributes, defStyleAttr) {
}
复制代码
math_parent
We set and wrap_content
see the effect in xml respectively :
<com.example.gitlinux.CustomView
android:background="#00ff00"
android:layout_width="match_parent"
android:layout_height="100dp"/>
<com.example.gitlinux.CustomView
android:background="#ff0000"
android:layout_width="wrap_content"
android:layout_height="100dp"/>
复制代码
Corresponding to:
As you can see, CustomView
the width setting math_parent
and the wrap_content
result are the same, why is this, let's find the answer from the source code
2. View
Measurement Specifications
Before looking at the source code, let's first understand View
the measurement specifications MeasureSpec
: the upper 2 bits represent Mode
and the lower 30 bits represent Size
.
MeasureSpec
There are three measurement modes:
AT_MOST
The current remaining allocated space of the parent View, the size isSize
, the size of the child View cannot exceedSize
, correspondingLayoutParams
to thewrap_content
mode inEXACTLY
The final size of the child View detected by the parent View isSize
the corresponding mode andLayoutParams
thematch_parent
specific value.UNSPECIFIED
The parent View has no restrictions on the view. It is as big as it needs to be. It is rarely used. This can be used to obtain the real width and height of the View. For exampleScrollView
, this measurement mode is specified on the height.
3. Why is the source code search the same as math_parent
the wrap_content
result:
首先我们先有个确定的概念:View最终的宽高
MeasureSpec
是由父View的MeasureSpec
和子View的LayoutParam
来决定的。
我们以ViewGroup
的measureChildWithMargins()
方法作为切入口
首先获取子View的LayoutParam
,调用了getChildMeasureSpec()
方法传入了子View的LayoutParam
和父View的宽高的MeasureSpec
:
getChildMeasureSpec()
方法比较长,我们主要截取下其中的关键逻辑:
当父View的测试模式为EXACTLY
时,子View的LayoutParam
如果为具体数值和match_parent
,则子View的MeasureSpec
测量规格就为EXACTLY
,否则为AT_MOST
当父View的测试模式为AT_MOST
时,子View的LayoutParam
如果为wrap_content
和match_parent
,则子View的MeasureSpec
测量规格就为AT_MOST
,否则为EXACTLY
。
我们用一个表格直观的看下子View的MeasureSpec
生成过程:
图片来自于Android进阶基础系列:View的工作原理 全面理解!
现在我们回到文章最开始的那个问题 :如果自定义View的宽度设置为wrap_content
,从上面的表格中可以看出不管父View的测量规格是AT_MOST
还是EXACTLY
,子View最终的宽度大小都是和宽度设置为match_parent
时的大小相同。
最终是怎么设置成子View的宽高大小的呢,具体的源码就不带着分析了,大体的调用逻辑就是:
Through
measureChildWithMargins()
the method just now, after obtaining the width and height ofMeasureSpec
the child View, call themeasure()
->onMeasure()
->setMeasuredDimension()
->setMeasuredDimensionRaw()
method of the child View, and finally complete the setting of the width and height of the child View.
3. How to solve math_parent
and wrap_content
the same result
This requires that when you customize the View, you need to rewrite the View onMeasure()
method. In this method, you can judge whether the current View's width or height is the layout parameter wrap_content
. If it is, you have to manually calculate the required real width and height, and then call the setMeasuredDimension()
setting.