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());
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.