Nested RecyclerView load second item with lag

615 Views Asked by At

I'm trying to make Recyclerview like Shareit app. It uses sticky header and something like Cardview. I searched and found somethings for implementing sticky header with itemdecoration and it's okay. But for Cardview part, I tried to make nested Recyclerview but performance was so bad, Because using two vertical Recyclerview is not good at all. Is there any better way to achieve this? The problem with performance is for when i scroll down. It makes lag on creating second item in parent Recyclerview.

enter image description here

AppheaderAdapter:

    @Override
    public AppHeaderAdapter.HeaderHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_media_header, parent, false);
        HeaderHolder headerHolder = new HeaderHolder(view);
        return headerHolder;
    }

class HeaderHolder extends RecyclerView.ViewHolder { //story
        final ImageView arrow;
        final TextView tarikh;
        final TextView tedad;
        final CheckBox tick;
        final RecyclerView item_recyc;

        HeaderHolder(View itemView) {
            super(itemView);

            arrow = itemView.findViewById(R.id.header_arrow);
            tarikh = itemView.findViewById(R.id.header_tarikh);
            tedad = itemView.findViewById(R.id.header_tedad);
            tick = itemView.findViewById(R.id.header_tick);
            item_recyc = itemView.findViewById(R.id.header_recyc);
        }
    }

@Override
    public void onBindViewHolder(@NonNull AppHeaderAdapter.HeaderHolder headerHolder, int position) {
        final AppHeader appHeader = appHeaders.get(position);

        ...

        GridLayoutManager gridLayoutManager = new GridLayoutManager(context, span);
        gridLayoutManager.setRecycleChildrenOnDetach(true);
        headerHolder.item_recyc.setLayoutManager(gridLayoutManager);
        headerHolder.item_recyc.setNestedScrollingEnabled(false);

        AppItemAdapter adapter = new AppItemAdapter(context, appHeader.getAppList(), fr_parent, width);
        headerHolder.item_recyc.swapAdapter(adapter, true);

        ...
    }

item_media_header.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/header_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    android:background="@drawable/so_round_so_white"
    app:cardCornerRadius="10dp"
    app:cardElevation="5dp">

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

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

            <ImageView
                android:id="@+id/header_arrow"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:padding="8dp"
                android:src="@drawable/header_arrow"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/header_tarikh"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="@dimen/fragment_file"
                android:textStyle="bold"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toRightOf="@id/header_arrow"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/header_tedad"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="2dp"
                android:textColor="#AAAAAA"
                android:textSize="@dimen/fragment_file"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toRightOf="@id/header_tarikh"
                app:layout_constraintTop_toTopOf="parent" />

            <CheckBox
                android:id="@+id/header_tick"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginRight="10dp"
                android:button="@null"
                android:clickable="false"
                android:drawableRight="@drawable/file_checkbox"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/header_recyc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>
</androidx.cardview.widget.CardView>

EDIT 1: I find that just first Cardview is loaded and second one is not loaded until I scroll to end of first one and then load second CardView item and it lags because of second RecyclerView. I found that Shareit use previous created adapter and just change its internal datalist, So i do that, but still has lag on second item.

Edite 2: I use one time created adapter on all RecyclerView but still has lag.

Edit 3: It's a known bug that innerRecyclerView load all items at first and that cause my lag too. because inner Recyclerview is inside NestedScrollView (RV) and it loads all content.Using constant height doesn't work, But I'll update question if I find something better.

2

There are 2 best solutions below

3
On

You can achieve this with ConstraintLayout and Flow.

EDIT: Sorry, I just realised I wrote everything in Kotlin. If you cannot understand something I can try to translate it to Java.

xml:

 <androidx.constraintlayout.widget.ConstraintLayout 
        android:id="@+id/constraintLayout"
        ...>

        <androidx.constraintlayout.helper.widget.Flow
            android:id="@+id/flow"
            app:flow_wrapMode="aligned"
            app:flow_horizontalStyle="spread_inside"
            app:flow_maxElementsWrap="4"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

Adding of items:

val constraintLayout = itemView.findViewById(R.id.constraintLayout)
val flow = itemView.findViewById(R.id.flow)
val items // items given to the viewHolder to display

val layoutInflater = LayoutInflater.from(context)

items.forEach { item ->
        val view = // create item view, e.g. DataBinding, ...
        view.id = ViewCompat.generateViewId()         
        constraintLayout.addView(root)
        flow.addView(root)
    }
}

// if there are less than 4 items you have to add invisible dummy items. Otherwise, the alignment will look different for these ViewHolders

if(items.size>=4) return

for (i in 0..(4-items.size)) {
        val view = // create dummy view
        view.id = ViewCompat.generateViewId()         
        constraintLayout.addView(root)
        flow.addView(root)
}


0
On

So far, Using vertical RV inside vertical RV has bug. Because of inner RV make long height and outer RV has no idea of this, So it create just first Item, but all items in inner RV. using all solution on Stack O.F. had no effect!