具有多视图滞后滚动的嵌套 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。
我有一个带有多个视图的 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。