具有多视图滞后滚动的嵌套 RecyclerView

Nested RecyclerView with Multiple View Lagging Scroll

我有一个带有多个视图的 recyclerView,其中每一行都是一个 recyclerView。当我滚动 recyclerView 时,创建的每一行都滞后。这是我的一些代码。在下面你可以看到主要 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);

您可以在下面看到适配器中绑定的内部 recyclerView 代码:

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

我使用 mRvPromotion.setNestedScrollingEnabled(false);ViewCompat.setNestedScrollingEnabled(childRecyclerView, false); 但存在滞后行为钢。 对于加载图像,我使用了 Fresco 并调整了它的大小配置。 如何防止滚动滞后?

编辑

这是我的适配器:

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;
}

}

例如我的 viewHolder PlaylistPromotionsVH 如下所示:

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);
}

}

在下面你可以看到主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>

更新

这是我用 fresco 加载图片的代码:

ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
            .setResizeOptions(new ResizeOptions(width, height))
            .build();
    simpleDraweeView.setController(
            Fresco.newDraweeControllerBuilder()
                    .setOldController(simpleDraweeView.getController())
                    .setImageRequest(request)
                    .build());

由于同时有滚动操作,所以出现这个问题是很自然的,为了解决这个问题,我建议禁用所有 child 的滚动操作,只需将这个 LayoutManger 添加到每个 child

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false){
            @Override
            public boolean canScrollVertically() {
                return false;
            }
        };

如果 child 保持网格布局管理器像这样禁用它

 new GridLayoutManager(getContext(),2){
            @Override
            public boolean canScrollVertically() {
                return false;
            }
        } ;

首先,您可以使用 setInitialItemPrefetchCount() 为嵌套的 RecyclerView 预取功能。有关更多信息,请查看此 article

我认为,主要原因是从服务器加载图像阻塞了主线程。您可以异步执行此操作。请也检查此 article