Why does the custom View need to deal with wrap_content

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_parentWe set and wrap_contentsee 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:

image.png

As you can see, CustomViewthe width setting math_parentand the wrap_contentresult are the same, why is this, let's find the answer from the source code

2. ViewMeasurement Specifications

Before looking at the source code, let's first understand Viewthe measurement specifications MeasureSpec: the upper 2 bits represent Modeand the lower 30 bits represent Size.
MeasureSpecThere are three measurement modes:

  • AT_MOSTThe current remaining allocated space of the parent View, the size is Size, the size of the child View cannot exceed Size, corresponding LayoutParamsto the wrap_contentmode in
  • EXACTLYThe final size of the child View detected by the parent View is Sizethe corresponding mode and LayoutParamsthe match_parentspecific value.
  • UNSPECIFIEDThe 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 example ScrollView, this measurement mode is specified on the height.

3. Why is the source code search the same as math_parentthe wrap_contentresult:

首先我们先有个确定的概念:View最终的宽高MeasureSpec是由父View的MeasureSpec和子View的LayoutParam来决定的。

我们以ViewGroupmeasureChildWithMargins()方法作为切入口

image.png 首先获取子View的LayoutParam,调用了getChildMeasureSpec()方法传入了子View的LayoutParam和父View的宽高的MeasureSpec

getChildMeasureSpec()方法比较长,我们主要截取下其中的关键逻辑:

image.png

image.png
当父View的测试模式为EXACTLY时,子View的LayoutParam如果为具体数值和match_parent,则子View的MeasureSpec测量规格就为EXACTLY,否则为AT_MOST image.png
当父View的测试模式为AT_MOST时,子View的LayoutParam如果为wrap_contentmatch_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 of MeasureSpecthe child View, call the measure()-> 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_parentand wrap_contentthe 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.

Guess you like

Origin juejin.im/post/7084826230285369357