StaggeredGridLayout中RecyclerView拖拽上移时无限循环问题
我之前也碰到过一模一样的问题!这其实是StaggeredGridLayoutManager的布局特性搞的鬼——当你向上拖拽item时,它的自动布局重排会把拖拽中的item“推”回原来的位置,形成循环。下面是几个亲测有效的解决方案:
1. 自定义StaggeredGridLayoutManager,禁用预测动画
StaggeredGrid的预测动画会在布局变化时提前计算item位置,很容易干扰拖拽过程。我们可以自定义LayoutManager来关闭这个特性:
public class NoPredictiveAnimStaggeredGridLayoutManager extends StaggeredGridLayoutManager { public NoPredictiveAnimStaggeredGridLayoutManager(int spanCount, int orientation) { super(spanCount, orientation); } @Override public boolean supportsPredictiveItemAnimations() { return false; } }
之后用这个自定义的LayoutManager替换原来的StaggeredGridLayoutManager,能大幅减少布局重排带来的位置错乱。
2. 在ItemTouchHelper回调中正确处理位置计算
默认的ItemTouchHelper.Callback的onMove方法没有适配StaggeredGrid的布局逻辑,我们需要手动计算目标位置,并且用正确的方式通知RecyclerView更新:
@Override public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) { int fromPosition = viewHolder.getAdapterPosition(); int toPosition = target.getAdapterPosition(); // 针对垂直StaggeredGrid处理上下移动逻辑,spanCount是你的布局列数 int spanCount = ((StaggeredGridLayoutManager) recyclerView.getLayoutManager()).getSpanCount(); // 交换数据集中的位置 if (fromPosition < toPosition) { for (int i = fromPosition; i < toPosition; i++) { Collections.swap(mDataList, i, i + 1); } } else { for (int i = fromPosition; i > toPosition; i--) { Collections.swap(mDataList, i, i - 1); } } // 必须用notifyItemMoved,不能用notifyDataSetChanged,避免全局布局重排 recyclerView.getAdapter().notifyItemMoved(fromPosition, toPosition); return true; }
这里的核心是避免全局刷新,只通知RecyclerView更新两个item的位置,减少StaggeredGrid自动调整的概率。
3. 拖拽过程中临时禁用自动布局
在拖拽开始时关闭StaggeredGrid的自动测量功能,阻止它在拖拽时重排布局;拖拽结束后再恢复。可以在ItemTouchHelper.Callback的onSelectedChanged和clearView方法中处理:
@Override public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) { super.onSelectedChanged(viewHolder, actionState); if (actionState == ItemTouchHelper.ACTION_STATE_DRAG && viewHolder != null) { // 开始拖拽时禁用自动布局 StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) viewHolder.itemView.getParent().getParent(); layoutManager.setAutoMeasureEnabled(false); } } @Override public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); // 拖拽结束后恢复自动布局 StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) recyclerView.getLayoutManager(); layoutManager.setAutoMeasureEnabled(true); // 强制刷新一次布局,确保最终位置正确 recyclerView.requestLayout(); }
这个方法能从根源上避免StaggeredGrid在拖拽过程中把item推回原位置。
4. 拖拽后修正滚动位置
如果拖拽后还是有位置偏移,可以在clearView方法中让RecyclerView滚动到拖拽后的item位置,确保显示正确:
@Override public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); // 滚动到当前item的最终位置 recyclerView.scrollToPosition(viewHolder.getAdapterPosition()); }
把这些方案组合起来用,基本就能解决StaggeredGridLayout RecyclerView向上拖拽循环的问题了。
内容的提问来源于stack exchange,提问作者Cheok Yan Cheng

