7

I have an issue using a ScrollView inside ConstraintLayout (constraint-layout:1.0.0-beta3)

The content of my ScrollView isn't showed entirely.

Here is my layout:

<android.support.constraint.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_test"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:background="#212121">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Constraint Layout"
            android:textSize="45sp"/>

    </LinearLayout>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/header"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="BUTTON"
                android:layout_marginTop="800dp"/>


        </LinearLayout>

    </ScrollView>

</android.support.constraint.ConstraintLayout>

And here is the result

enter image description here

As you can see the button isn't visible and I reached the bottom of my ScrollView.

It seems to works well with LinearLayout with layout below

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_test"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:background="#212121">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Linear Layout"
            android:textSize="45sp"/>

    </LinearLayout>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="BUTTON"
                android:layout_marginTop="800dp"/>


        </LinearLayout>

    </ScrollView>

</LinearLayout>

And result is

enter image description here

With LinearLayout end of ScrollView is reachable.

Is there a bug with ConstraintLayout or do I made something wrong?

guillaume
  • 1,638
  • 5
  • 24
  • 43
  • 1
    That `android:layout_marginTop="800dp"` on the button looks a little sketchy... Did you want the button to always show at the bottom of the screen and have the `ScrollView` sit on top of that? – kris larson Nov 16 '16 at 16:45
  • This marginTop is an example to force the Button to be visible only after scrolling to bottom of ScrollView. I wanted to illustrate the behavior with a tiny example. – guillaume Nov 16 '16 at 16:49

2 Answers2

11

The way I would have done it is:

<android.support.constraint.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_test"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/header"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:background="#212121"
        android:text="Constraint Layout"
        android:textSize="45sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ScrollView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:fillViewport="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/header">

        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom"
                android:text="BUTTON"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent" />

        </android.support.constraint.ConstraintLayout>

    </ScrollView>

</android.support.constraint.ConstraintLayout>

Which gives:

enter image description here

A few things to keep in mind:

  • you don't need a linearlayout for the header, you can just use the textview directly.
  • don't use match_parent, it might seem to work, but is undefined. Use 0dp instead with the correct constraints to stretch the view as you want.
  • clearly, don't use a 800dp margin. It might look ok on your particular screen, but won't give you what you want on different devices.
  • scrollview by default will wrap its content -- the fillViewport attribute is here to make it takes the indicated space
  • you can use nested ConstraintLayout when it makes sense. In the future, we might also take advantage of it to do some performance improvements
Nicolas Roard
  • 8,339
  • 1
  • 28
  • 30
-1

I changed the height of the ScrollView to match_parent and added a bottom constraint. This seemed to make it work correctly.

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/header"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent">

Note: Having a height of wrap_content on a vertically scrolling view usually doesn't end well, in any layout.

kris larson
  • 30,387
  • 5
  • 62
  • 74
  • Indeed, my issue was coming from there. Thank you! – guillaume Nov 16 '16 at 17:33
  • actually -- match_parent is NOT supported in ConstraintLayout, so it won't do what you think it does. It might seem to work in your example, but it likely will be broken on other devices. You **can** replicate the match_parent behavior though, but you have to explicitly define the constraints. See http://stackoverflow.com/questions/40633299/android-constraint-layout-strange-behavior/40662039#40662039 – Nicolas Roard Nov 19 '16 at 18:11
  • @NicolasRoard I had to do both things -- set the height to `match_parent` and add the bottom constraint -- to make the layout correct. Doing either one or the other was not sufficient. – kris larson Nov 20 '16 at 07:17
  • @krislarson see my answer -- match_parent is undefined with ConstraintLayout. – Nicolas Roard Nov 20 '16 at 11:05
  • You can use `0dp` instead of `match_parent` for layout_height and it works well. – ciaranodc Jun 01 '21 at 10:40