反复改变子视图的可见性和测量父视图会导致测量尺寸和显示的错误结果

Ric*_*ard 5 android android-layout android-view android-viewgroup android-measure

为什么当您反复更改包含在父视图中的子视图的可见性并测量父视图时,Android 会返回错误的结果?

我创建了一个简单的测试:一个只有一个 ConstraintLayout 和两个 TextView 的 XML 文件。我会反复将最后一个 TextView 的可见性更改为 GONE 和 VISIBLE。每次我改变 TextView 的可见性时,我都会测量 ConstraintLayout 的宽度和高度。为了改变 TextView 的可见性,我在 ConstraintLayout 上设置了一个点击监听器。

这是上述简单布局的 XML:

<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/testLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginTop="16dp"
    android:layout_marginEnd="16dp"
    android:padding="16dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:background="@color/colorPrimary">

    <TextView
        android:id="@+id/iWillShow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:textColor="@color/colorOnPrimary"/>

    // This one will be hidden on click
    <TextView
        android:id="@+id/hideMe"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="World"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/iWillShow"
        android:textColor="@color/colorOnPrimary" />
</androidx.constraintlayout.widget.ConstraintLayout>
Run Code Online (Sandbox Code Playgroud)

在我的 Kotlin 文件中,我有以下内容:

testLayout.setOnClickListener {
    val matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec((it.parent as View).width, View.MeasureSpec.EXACTLY)
    val wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
    hideMe.visibility = View.VISIBLE
    it.measure(matchParentMeasureSpec, wrapContentMeasureSpec)
    Log.d("TEST LAYOUT", it.measuredWidth.toString() + " " + it.measuredHeight.toString())
    hideMe.visibility = View.GONE
    it.measure(matchParentMeasureSpec, wrapContentMeasureSpec)
    Log.d("TEST LAYOUT", it.measuredWidth.toString() + " " + it.measuredHeight.toString())
    hideMe.visibility = View.VISIBLE
    it.measure(matchParentMeasureSpec, wrapContentMeasureSpec)
    Log.d("TEST LAYOUT", it.measuredWidth.toString() + " " + it.measuredHeight.toString())
    hideMe.visibility = View.GONE
    it.measure(matchParentMeasureSpec, wrapContentMeasureSpec)
    Log.d("TEST LAYOUT", it.measuredWidth.toString() + " " + it.measuredHeight.toString())
}
Run Code Online (Sandbox Code Playgroud)

单击父视图后,日志将准确返回以下内容:

D/TEST LAYOUT: 1080 186
D/TEST LAYOUT: 1080 135
D/TEST LAYOUT: 1080 186
D/TEST LAYOUT: 1080 186
Run Code Online (Sandbox Code Playgroud)

显然,上面的日志是不正确的。它应该D/TEST LAYOUT: 1080 135在最后一行返回。然而,情况似乎并非如此。此外,它还导致错误显示。父视图的高度如下(这是错误的,它应该更小):

高度不正确

当我尝试只将 TextView 的可见性设置为 GONE 而不进行测量时(像这样):

testLayout.setOnClickListener {
    hideMe.visibility = View.GONE
}
Run Code Online (Sandbox Code Playgroud)

它返回正确的高度显示,如下所示:

正确的高度

为什么会出现以上情况?背后的过程是怎样的?设置 GONE -> VISIBLE 一次后出现问题。出现一次设置 GONE -> VISIBLE 后,将其设置为 GONE -> VISIBLE -> GONE ... 将产生该错误。