Identify first visible view inside viewgroup

Viewed 36

I have several TextViews inside ConstraintLayout. The visibility of those TextViews are set at runtime based upon the data availability.

I need to bold the text of the first visible TextView.

I have tried many things but couldn't resolve the issue.

I have tried:

  1. Looping through the child inside the parent layout and finding the first child and making it bold. This approach always finds the first child inside the parent layout regardless of it's visiblility.
  2. I put a check before retrieving the first element if view.visibility == View.VISIBLE and also I checked view.isShown, both way again returns the first child in the view hierarchy irrespective of the visibility. So, view.visibility always returns View.VISIBLE.

What am I missing and how can I make it work?

Providing sample codes:

XML

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="viewModel"
            type="MyViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/rootLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="24dp">

        <TextView
            android:id="@+id/textView1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="First textview"
            android:textColor="@color/black"
            android:visibility="@{viewModel.textView1Visibility}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />


        <TextView
            android:id="@+id/textView2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Second textview"
            android:textColor="@color/black"
            android:visibility="@{viewModel.textView2Visibility}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/textView1" />

        <TextView
            android:id="@+id/textView3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Third textview"
            android:textColor="@color/black"
            android:visibility="@{viewModel.textView3Visibility}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/textView2" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

CODE

    binding.apply {

        /** We have three textviews.
         * textView1 doesn't have data so we are hiding it.
         * textView2 has data so, we need to show it.
         * and textView2 will be the first item, we need to bold it as well. */

        textView1.visibility = View.GONE

        /** looping through the children of root layout and bolding the first visible */
        rootLayout.forEach exit@ { view ->
            if(view.isVisible || view.isShown) {
                (view as TextView).typeface = Typeface.DEFAULT_BOLD
                return@exit
            }
        }

    }
1 Answers

Your code is looping through all the children of the ConstraintLayout because return@exit acts like a continue and not a true break.

Try the following:

run exit@{
    rootLayout.forEach { view ->
        if (view.isVisible || view.isShown) {
            (view as TextView).typeface = Typeface.DEFAULT_BOLD
            return@forEach
        }
    }
}

There may be a more elegant way to go about this, but the above should work.

Update to code:

rootLayout.children.firstOrNull { view ->
    view.isVisible || view.isShown
}?.also { view ->
    (view as TextView).typeface = Typeface.DEFAULT_BOLD
}

I will also mention that your code will find the first visible child of the ConstraintLayout child order, but that view may not be the view that one would pick as the first looking at the screen. That is because the view order within the children of ConstraintLayout may differ from the on-screen order due to layout constraints. This may not be a problem for you, but it is something to think about.

Related