DevW
DevW

Reputation: 33

How to scroll NestedScrollView with a ListView inside?

I'm creating an activity in which I need to show several elements and a listview, and I want that when the user does scroll I want to scroll all the activity not only the listview, for this, I implemented a NestedScrollView, but the scroll does not work, it remains without scrolling, do I need an attribute? or is there a better way to do what I need to do?

I attach my XML code:

<androidx.core.widget.NestedScrollView 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"
    android:fillViewport="true">

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

       <!-- Other items -->

        <ListView
            android:id="@+id/lvAC"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" />


    </androidx.constraintlayout.widget.ConstraintLayout>


</androidx.core.widget.NestedScrollView>

Upvotes: 2

Views: 1135

Answers (2)

mohammadReza Abiri
mohammadReza Abiri

Reputation: 1799

try this way:

<?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="wrap_content">
        <View
            android:id="@+id/view"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:background="@drawable/ic_launcher_background"/>


        <ListView
            android:id="@+id/lvAC"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/view"
            android:nestedScrollingEnabled="true"
            />
    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

and set this method to your listview :

public static void setListViewHeightBasedOnChildren(ListView listView) {
        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter == null) {
            // pre-condition
            return;
        }

        int totalHeight = 0;
        for (int i = 0; i < listAdapter.getCount(); i++) {
            View listItem = listAdapter.getView(i, null, listView);
            listItem.measure(0, 0);
            totalHeight += listItem.getMeasuredHeight();
        }

        ViewGroup.LayoutParams params = listView.getLayoutParams();
        params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
        listView.setLayoutParams(params);
        listView.requestLayout();
    }

Upvotes: 1

Chris
Chris

Reputation: 1220

Nesting a ListView inside a ScrollView is problematic as it causes multiple vertical scrolling behaviors which is confusing to the user. Instead, use a single RecyclerView that can provide multiple ViewTypes.

Your RecyclerView adapter should implement getItemViewType, returning the number of different views you need to display. For instance, if you have:

<androidx.core.widget.NestedScrollView 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"
    android:fillViewport="true">

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

        <!-- Other items -->
        <View
            android:id="@+id/view1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            />
        <View
            android:id="@+id/view2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toBottomOf="@id/view1"
            app:layout_constraintStart_toStartOf="parent"
            />

        <ListView
            android:id="@+id/lvAC"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintTop_toBottomOf="@id/view2"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" />


    </androidx.constraintlayout.widget.ConstraintLayout>


</androidx.core.widget.NestedScrollView>

You should have a layout like:

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
    />

And your adapter should be something like:

class MyViewHolder(val rootView: View) : RecyclerView.ViewHolder(rootView)

class MyAdapter(val items: List<Any>) : RecyclerView.Adapter<MyViewHolder>() {


    companion object {
        const val TYPE_VIEW1 = 1
        const val TYPE_VIEW2 = 2
        const val TYPE_ITEMS = 3
    }

    override fun getItemViewType(position: Int): Int = when (position) {
        0 -> TYPE_VIEW1
        1 -> TYPE_VIEW2
        else -> TYPE_ITEMS
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder = when (viewType) {
        TYPE_VIEW1 -> MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_view1, parent, false))
        TYPE_VIEW2 -> MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_view2, parent, false))
        else -> MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_other, parent, false))
    }

    override fun getItemCount(): Int = 2 + items.size

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
}

Upvotes: 1

Related Questions