Why is it that when you repeatedly alter the visibility of a child view that is contained within a parent view and measure the parent view, Android returns wrong results?

I created a simple test: an XML file with only one ConstraintLayout and two TextViews. I will repeatedly alter the visibility of the last TextView to GONE and VISIBLE. Each time I alter the visibility of the TextView, I will measure the ConstraintLayout's width and height. To alter the visibility of the TextView, I set a click listener on the ConstraintLayout.

Here's the XML for said simple layout:

<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>

In my Kotlin file, I have the following:

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())
}

Upon clicking the parent view, the logs will return exactly these:

D/TEST LAYOUT: 1080 186
D/TEST LAYOUT: 1080 135
D/TEST LAYOUT: 1080 186
D/TEST LAYOUT: 1080 186

Obviously, the above logs are incorrect. It should have returned D/TEST LAYOUT: 1080 135 on the last line. However, that doesn't seem to be the case. Furthermore, it also caused an erroneous display. The height of the parent view will be the following (this is wrong, it should have been smaller):

Incorrect Height

When I tried only setting the visibility of the TextView to GONE without the measuring (like this):

testLayout.setOnClickListener {
    hideMe.visibility = View.GONE
}

It returns the correct display of height like below:

Correct Height

Why does the above happen? What's the process behind it? The problem occurs after setting GONE -> VISIBLE once. After one occurrence of setting GONE -> VISIBLE, setting it to GONE -> VISIBLE -> GONE ... will produce that error.

0

There are 0 answers