0

I have the following structure in my fragment layout file:

- ScrollView
  - ConstraintLayout
    - CardView
      - *some stuff here*
    - CardView
      - ListView
        - *list header*
        - *list items generated with a custom adapter*

If I remove the outer ScrollView, I can see the whole content of the ListView, and if it's bigger than the remaining space for the 2nd CardView, I can scroll it. The 1st CardView stays in place, but the content of the 2nd one is scrollable.

However, I would like to scroll the whole fragment. I would like the 2nd CardView to expand and contain the whole ListView, and if I scroll up or down, the 1st one moves as well.

I tried several combinations of height settings. No point of showing you my actual layout XML, because it's a mess. I would like a clean slate. Is it possible to achieve?

EDIT:

I know the ListView is a scroll container itself, but I think it's a pretty common need to scroll the whole thing, so I can't understand why it's so hard to make it work.

Nekomajin42
  • 429
  • 1
  • 5
  • 14

1 Answers1

1

Alright, after combining multiple answers, I have the solution that I needed.

First

I needed to use a NestedScrollView instead of a regular ScrollView.

It solves the conflict between the two scroll containers (ScrollView and ListView).

Reference: Android: ScrollView vs NestedScrollView

NOTE: My list content is dynamic, so it can be too short to fill the remaining space. I had to set android:fillViewport="true" on the NestedScrollView. If the list is longer than the remaining space, it will not cause any trouble.

layout.xml

<?xml version="1.0" encoding="utf-8"?>

<androidx.core.widget.NestedScrollView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true">

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

        <androidx.cardview.widget.CardView
            android:id="@+id/card1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        <!-- NOTE: constraints properties are missing from here for easier reading -->

            <!-- card content here -->

        </androidx.cardview.widget.CardView>

        <androidx.cardview.widget.CardView
            android:id="@+id/card2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        <!-- NOTE: constraints properties are missing from here for easier reading -->

            <ListView
                android:id="@+id/list"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
            <!-- NOTE: this will change in step 3 -->

        </androidx.cardview.widget.CardView>

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.core.widget.NestedScrollView>

Second

Following the steps above will make the ListView collapse to the height of its first item. To solve this, I needed to create a subclass from ListView and override its onMeasure() method, so it can calculate the proper height at runtime.

Reference: Android - NestedScrollView which contains ExpandableListView doesn't scroll when expanded

NonScrollListView.java

package my.package.name

import ...

public class NonScrollListView extends ListView {
    
    // NOTE: right click -> create constructors matching super

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
        ViewGroup.LayoutParams params = getLayoutParams();
        params.height = getMeasuredHeight();
    }
}

Third

I needed to use my custom View instead of the regular ListView in my layout XML.

layout.xml excerpt

<my.package.name.NonScrollListView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

This way I managed to make it work. Both cards move together on scroll, even if I tap the ListView area.

I don't know whether it causes performance issues with really long lists, because mine contains a few dozen items at most, but I have no problem on a low end Galaxy A20e.

Nekomajin42
  • 429
  • 1
  • 5
  • 14