Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested RecyclerView with Multiple View Lagging Scroll

I have a recyclerView with multiple view that each row of that is a recyclerView. When I scroll recyclerView, that is laggy on each row created. Here is some of my code. At below you can see config of main recyclerView:

LinearLayoutManager mLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
    mRvPromotions.setLayoutManager(mLayoutManager);
    mRvPromotions.setItemViewCacheSize(20);
    mRvPromotions.setDrawingCacheEnabled(true);
    mRvPromotions.setHasFixedSize(true);
    mRvPromotions.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
    mRvPromotions.setAdapter(adapter);

and you can see code of inner recyclerView that bind in adapter at below:

mRvPromotion.setLayoutManager(new GridLayoutManager(itemView.getContext(), 3));
    mRvPromotion.setItemViewCacheSize(20);
    mRvPromotion.setDrawingCacheEnabled(true);
    mRvPromotion.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
    adapter = new PlaylistPromotionsRVAdapter(new PlaylistModelCallBack(), new PlaylistPromotionVHFactory());
    mRvPromotion.setAdapter(adapter);
    adapter.submitList(getVM().getPlaylist());

I use mRvPromotion.setNestedScrollingEnabled(false); and ViewCompat.setNestedScrollingEnabled(childRecyclerView, false); but laggy behaviour steel exist. For load image I used Fresco and resizing config of that. How can I prevent from lagging scroll?

EDIT

Here is my adapter:

public class PlaylistRVAdapter extends ListAdapter<PlayListPromotionAndSlider, BaseViewHolder> {

private final PlaylistPageVHFactory mFactory;
private final PublishSubject<PlaylistSliderVHAction> mClickSliderPS = PublishSubject.create();
private final PublishSubject<PlaylistPromotionsVHAction> mClickPromotionPS = PublishSubject.create();

@Inject
PlaylistRVAdapter(@NonNull PlaylistPromotionModelCallBack diffCallback, PlaylistPageVHFactory factory) {
    super(diffCallback);
    mFactory = factory;
}

@NonNull
@Override
public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    return mFactory.create(parent, viewType);
}

@Override
public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
    switch (getItemViewType(position)) {
        case 0:
            PlaylistSliderVH sliderVH = (PlaylistSliderVH) holder;
            sliderVH.getVM().setObject(getItem(position).getSlide());
            sliderVH.bind();
            sliderVH.itemOnClick(mClickSliderPS);
            break;
        case 1:
            PlaylistPromotionsVH promotionsVH = (PlaylistPromotionsVH) holder;
            promotionsVH.getVM().setObject(getItem(position).getPromotion());
            promotionsVH.bind();
            promotionsVH.itemOnClick(mClickPromotionPS);
            break;
    }
}

public Observable<PlaylistSliderVHAction> getClickPS() {
    return mClickSliderPS;
}

public PublishSubject<PlaylistPromotionsVHAction> getmClickPromotionPS() {
    return mClickPromotionPS;
}

@Override
public int getItemViewType(int position) {
    return getItem(position).getSlide() != null ? 0 : 1;
}

}

and for example my viewHolder PlaylistPromotionsVH is like below:

public class PlaylistPromotionsVH extends BaseViewHolder<PlaylistPromotionsVHAction, PlaylistMasterModel, PlaylistPromotionsVM> {

@BindView(R.id.txtPromotionTitle)
AppCompatTextView mTxtPromotionTitle;
@BindView(R.id.txtMore)
AppCompatTextView mTxtMore;
@BindView(R.id.rvPromotion)
RecyclerView mRvPromotion;

PlaylistPromotionsRVAdapter adapter;

public PlaylistPromotionsVH(View itemView, PlaylistPromotionsVM viewModel) {
    super(itemView, viewModel);
    ButterKnife.bind(this, itemView);
}

@Override
public void bind() {
    mTxtPromotionTitle.setText(mVM.getPlaylistName());
    mRvPromotion.setLayoutManager(new GridLayoutManager(itemView.getContext(), 3));
    mRvPromotion.setItemViewCacheSize(20);
    mRvPromotion.setDrawingCacheEnabled(true);
    mRvPromotion.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
    mRvPromotion.setHasFixedSize(true);
    int spacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12,
            itemView.getContext().getResources().getDisplayMetrics());
    mRvPromotion.addItemDecoration(new RecyclerView.ItemDecoration() {
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            int position = parent.getChildAdapterPosition(view); // item position
            int spanCount = 3;
            if (position >= 0) {
                int column = position % spanCount; // item column

                outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
                outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)

                if (position < spanCount) { // top edge
                    outRect.top = spacing;
                }
                outRect.bottom = spacing; // item bottom
            } else {
                outRect.left = 0;
                outRect.right = 0;
                outRect.top = 0;
                outRect.bottom = 0;
            }
        }
    });

    adapter = new PlaylistPromotionsRVAdapter(new PlaylistModelCallBack(), new PlaylistPromotionVHFactory());
    mRvPromotion.setAdapter(adapter);

    adapter.submitList(getVM().getPlaylist());
}

@Override
public void itemOnClick(PublishSubject<PlaylistPromotionsVHAction> actionSubject) {
    RxView.clicks(mTxtMore)
            .map(o -> new PlaylistPromotionsVHAction(getAdapterPosition()))
            .repeat()
            .subscribe(actionSubject);
}

}

at below you can see layout of each row of main recyclerView:

<RelativeLayout 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="wrap_content"
android:layout_marginBottom="16dp"
android:background="@color/colorPromotionBackground"
android:descendantFocusability="blocksDescendants"
app:layout_behavior="@string/appbar_scrolling_view_behavior">

<RelativeLayout
    android:id="@+id/rlPromotionTitle"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/txtPromotionTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="16dp"
        android:layout_toRightOf="@+id/txtMore"
        android:gravity="right"
        android:textColor="@android:color/white"
        tools:ignore="RtlHardcoded" />

    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/txtMore"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_centerVertical="true"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:background="@drawable/ic_see_more" />
</RelativeLayout>
<android.support.v7.widget.RecyclerView
    android:id="@+id/rvPromotion"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@+id/rlPromotionTitle"
    android:focusableInTouchMode="true"
    android:overScrollMode="never"
    android:scrollbars="none"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</RelativeLayout>

UPDATE

Here is my code of loading image with fresco:

ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
            .setResizeOptions(new ResizeOptions(width, height))
            .build();
    simpleDraweeView.setController(
            Fresco.newDraweeControllerBuilder()
                    .setOldController(simpleDraweeView.getController())
                    .setImageRequest(request)
                    .build());
like image 726
Masoud Mokhtari Avatar asked Sep 04 '18 09:09

Masoud Mokhtari


1 Answers

Firstly, you can use setInitialItemPrefetchCount() for prefetching feature for nested RecyclerView. for more information please check this article

I think, the main reason, loading image from server blocks the main thread. You can do this asynchronously. please check this article too.

like image 77
ziLk Avatar answered Sep 24 '22 04:09

ziLk