1

I have two TextViews and a ScrollView nested in a ConstraintLayout. Inside the ScrollView there is a TableLayout. The ScrollView's left, right and bottom constraints are set to "parent". The top constraint is set to the TextView just before it. Here is the XML:

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom">

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:text="TextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        android:textSize="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView6">

        <TableLayout
            android:layout_width="fill_parent"
            android:layout_height="match_parent"
            android:layout_marginStart="32dp"
            android:layout_marginTop="32dp"
            android:layout_marginEnd="32dp"
            android:layout_marginBottom="32dp"
            android:gravity="center"
            android:stretchColumns="0,1,2,3,4">

            <TableRow
                android:layout_width="fill_parent"
                android:layout_weight="1">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:text="1" />
                    ... 4 more TextViews
            </TableRow>
            ... many more TableRows
        </TableLayout>
    </ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

The problem is that the ScrollView's width does not match its constraints. Instead, when more rows are added such that the ScrollView is taller than the its parent's remaining height, it overlaps the TextView just before it. The two screenshots below illustrate the problem:

No overlap:

enter image description here

Overlap:

enter image description here

When I set the ScrollView height to "0dp" (match constraints) it renders literally as 0dp (The ScrollView shrinks and none of its contents are shown).

What should I change to make the ScrollView expand and fill the remaining height of its parent container?

Wais Kamal
  • 5,858
  • 2
  • 17
  • 36

2 Answers2

1

You need to correctly constrain your widgets for ConstraintLayout to be able to correctly measure and laid out your views.

In short:

  1. The Top TextView needs to be the head of the vertical chain (app:layout_constraintVertical_chainStyle="packed"), and must have a bottom constraint (bottom_to_Topof...):
    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:text="TextView"
        app:layout_constrainedHeight="true"
        app:layout_constraintVertical_chainStyle="packed"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/textView6" />

The textView is constrainedHeight = true because you want it to wrap_content, and widgets that have text, can have some problems when wrapped in constraint layouts (see this other answer for more info about why that may be needed).

The Second TextView, needs to be pinned as well to some bottom, in this case the scrollView...

    <TextView
        android:id="@+id/textView6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constrainedHeight="true"
        android:text="TextView"
        android:textSize="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2"
        app:layout_constraintBottom_toTopOf="@id/scrollView" />

Now they are chained together, but the ScrollView needs some action...

Simply pin the scrollview to the bottom, so it can pull from somewhere, and give it the 0dp you need, so it can calculate its size/position based on the constrains.

<ScrollView
        android:id="@+id/scrollView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView6">

(also give it a name/id...)

This causes CL to calculate space/sizing correctly and to avoid the overlaps you complain about, since it now has clear rules about what can go where.

When you omit a constrain (like bottom), especially when there are stupid widgets around like ScrollViews, you can have unpredictable behavior based on the contents, something that is hard to anticipate and fix. Always, when in doubt, try to give all widgets all the constrains you can.

Martin Marconcini
  • 26,875
  • 19
  • 106
  • 144
  • Thank you, but the problem persists. 0dp is rendering literally "0dp" (zero height). However 0dp width renders correctly (expands to fill parent). – Wais Kamal Mar 19 '21 at 17:47
  • It's hard to see what your issue is without looking at the full picture. 0dp does not mean zero height, it's a special value used by CL to know that certain widget's size will be determined by its constraints (and, naturally, by the other views in the layout). If the scroll view renders with "0 height" then that means you are missing a constraint, or CL does NOT have enough information to calculate it; this can be due to multiple reasons, even timing can be one of them; depending when you "fill" the Scroll view, will also affect this. – Martin Marconcini Mar 22 '21 at 10:58
  • Thanks so much, yes something was wrong with the constraints. – Wais Kamal Mar 22 '21 at 16:23
1

I would suggest several points (I couldn't figure out which one is critical):

1. You shouldn't use match_parent for child views of a ConstraintLayout. Instead, use '0dp'.

Official training:

Note: You cannot use match_parent for any view in a ConstraintLayout. Instead use "match constraints" (0dp).

2. You should not use @+id/xxx but use @id/xxx to specify other views. The former is for creating a new id.

  1. Change ConstraintLayout's layout_height to match_parent.

  2. Change TableLayout's layout_height to wrap_content.

My result:

screenshot

layout.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="bottom">

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginTop="16dp"
            android:layout_marginEnd="16dp"
            android:text="TextView"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/textView6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView"
            android:textSize="16dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/textView2" />

        <ScrollView
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/textView6">

            <TableLayout
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="32dp"
                android:layout_marginTop="32dp"
                android:layout_marginEnd="32dp"
                android:layout_marginBottom="32dp"
                android:gravity="center"
                android:stretchColumns="0,1,2,3,4">

                <TableRow
                    android:layout_width="fill_parent"
                    android:layout_weight="1">

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:text="1" />

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:text="2" />

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:text="3" />

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:text="4" />

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:text="5" />
                </TableRow>

               <!-- plenty of TableRows... -->

            </TableLayout>
        </ScrollView>
    </androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
hata
  • 11,633
  • 6
  • 46
  • 69
  • The IDE (Android Studio) itself uses `@+id` both for creating a new id and referencing to an existing id, but actually including the plus sign or omitting it when referencing a view doesn't matter. As for the height, setting it (and the width) to 0dp does not solve the problem - the result is as stated in the question. – Wais Kamal Mar 19 '21 at 17:33
  • @WaisKamal Weird... In my layout editor, it seems ok (no overlap). – hata Mar 20 '21 at 04:01
  • Do you mean there is no overlap in the photo you attached? Can you try adding more rows such that the table is taller than the available height? This is where the overlap happens. I am not sure if it matters, but the ConstraintLayout is nested in a FrameLayout. – Wais Kamal Mar 20 '21 at 11:49
  • Can you please show your code? You might be missing something, because I get a totally different result (0 height when using 0dp). Also, the ConstraintLayout has to expand based on its content (up to a certain height set programatically) so setting its height to match_parent is not convenient here. Even though if I set it this way it does not render correctly. – Wais Kamal Mar 21 '21 at 18:57
  • @WaisKamal Please check this out. – hata Mar 22 '21 at 06:24
  • Oh man, this works. I've just realized I've screwed up the TextView constraints. Thank you so much for your time! – Wais Kamal Mar 22 '21 at 16:22