RecyclerView通过itemTouchHelper拖放快速拖动时很奇怪
Posted
技术标签:
【中文标题】RecyclerView通过itemTouchHelper拖放快速拖动时很奇怪【英文标题】:RecyclerView drag & drop via itemTouchHelper bahaving strange when dragging fast 【发布时间】:2016-05-18 08:36:29 【问题描述】:我一直在关注此链接"Drag and Swipe with RecyclerView - by IPaulPro" 的指南,在某些情况下我遇到了一些问题。
所以,我基本上做了他解释的所有事情 + 我在 item 中添加了一个 TextView,它表示 RecyclerView 中 item 的位置,如下所示:
一切看起来都不错,除非我开始快速“弹射”物品,然后我有两个问题:
它恰好有重复的数字。
我所做的是在
onItemClear()
方法 - 它以某种方式修复它,但导致
IllegalStateException
,抓住了 - 会导致
IndexOutOfBounds
异常。
有时,当滑动过快时,项目会进入“背景”。仅当项目大小不同时才能看到这一点。
我将把我的整个适配器代码粘贴在下面,其中一定有缺陷。
LayoutInflater inflater;
Context context;
androidEntityQuestionResult androidEntityQuestionResult;
ArrayList<AndroidEntityAnswer> list = new ArrayList<>();
ORDLayoutManagerQuestion ord;
ScreenDimensionsConstants sdc;
public OrderingRecycleAdapter(Context context, AndroidEntityQuestionResult androidEntityQuestionResult, ORDLayoutManagerQuestion ord)
inflater = LayoutInflater.from(context);
this.context = context;
this.list = androidEntityQuestionResult.getAndroidEntityQuestion().getEntityAnswer();
this.androidEntityQuestionResult = androidEntityQuestionResult;
this.ord = ord;
sdc = new ScreenDimensionsConstants(context);
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
View view = inflater.inflate(R.layout.custom_row_ordering_rv, parent, false);
final RecyclerView.ViewHolder holder = new OrderingViewHolder(view);
return holder;
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
if (holder instanceof OrderingViewHolder)
((OrderingViewHolder) holder).answerText.setText(list.get(position).getAnswer().getANSWER_TEXT());
int currentPosition = position + 1;
((OrderingViewHolder) holder).position.setText("#" + currentPosition);
@Override
public int getItemCount()
return list.size();
@Override
public boolean onItemMove(int fromPosition, int toPosition)
if (fromPosition < toPosition)
for (int i = fromPosition; i < toPosition; i++)
Collections.swap(list, i, i + 1);
else
for (int i = fromPosition; i > toPosition; i--)
Collections.swap(list, i, i - 1);
notifyItemMoved(fromPosition, toPosition);
notifyItemChanged(fromPosition);
return true;
@Override
public void onItemDismiss(int position)
@Override
public void onStartDrag(RecyclerView.ViewHolder viewHolder)
ord.getItemTouchHelper().startDrag(viewHolder);
class OrderingViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder
private TextView answerText;
private ImageView pin;
private TextView position;
public OrderingViewHolder(View itemView)
super(itemView);
answerText = (TextView) itemView.findViewById(R.id.orderingAnswer);
answerText.setTextSize(TypedValue.COMPLEX_UNIT_PX, sdc.getHeight() / 40);
pin = (ImageView) itemView.findViewById(R.id.ordering_pin);
pin.getLayoutParams().width = sdc.getHeight() / 15;
pin.getLayoutParams().height = sdc.getHeight() / 15;
pin.setOnTouchListener(new View.OnTouchListener()
@Override
public boolean onTouch(View v, MotionEvent event)
if (MotionEventCompat.getActionMasked(event) ==
MotionEvent.ACTION_DOWN)
OrderingRecycleAdapter.this.onStartDrag(OrderingViewHolder.this);
return false;
);
position = (TextView) itemView.findViewById(R.id.answer_position);
position.setTextSize(TypedValue.COMPLEX_UNIT_PX, sdc.getHeight() / 40);
@Override
public void onItemSelected()
itemView.setBackgroundResource(R.drawable.menu_item_background_ice_blue);
@Override
public void onItemClear()
itemView.setBackgroundResource(R.drawable.menu_item_background_white);
int currentPosition = getLayoutPosition() + 1;
position.setText("#" + currentPosition);
//notifyDataSetChanged();
额外问题
是否有任何与 2 个 RecyclerView 之间的拖放相关的教程或任何信息?
我知道有一个关于 SO 的问题,但没有答案,我在这里可能更幸运。
【问题讨论】:
我也在使用您上面提到的链接中的代码,当我交换得太快时我也遇到了麻烦,但我通过覆盖从 ItemTouchHelper.Callback 扩展的类中的 onSelectedChanged 解决了这个问题。 你用什么代码覆盖了它?我使用了来自github.com/iPaulPro/Android-ItemTouchHelper-Demo/blob/master/… 的代码 您想发布我的代码并使用它吗?或者你想要修复? 我不想看到“我不想要代码,我想要修复”这就是我问的原因。 你能分享一下 Screendimensionsconstants 和 ORDLayoutManagerQuestion 类吗?由于 itemtouchhelper,ORDLayoutManagerQuestion 尤其令人感兴趣。也许您可以尝试在 onbindviewholder 上移动您的触摸监听器。 【参考方案1】:改变你的 onitemmove 方法
@Override
public boolean onItemMove(int fromPosition, int toPosition)
if (fromPosition < toPosition)
for (int i = fromPosition; i < toPosition; i++)
Collections.swap(list, i, i + 1);
else
for (int i = fromPosition; i > toPosition; i--)
Collections.swap(list, i, i - 1);
notifyItemMoved(fromPosition, toPosition);
notifyItemChanged(fromPosition);
return true;
到:
@Override
public boolean onItemMove(int fromPosition, int toPosition)
if (fromPosition < toPosition)
for (int i = fromPosition; i < toPosition; i++)
Collections.swap(list, i, i + 1);
else
for (int i = fromPosition; i > toPosition; i--)
Collections.swap(list, i, i - 1);
notifyItemMoved(fromPosition, toPosition);
return true;
【讨论】:
不工作,你必须以这种方式逐个位置拖动它,这不是我想要实现的。 不,您不想逐个位置拖动它,只需尝试此代码并告诉我 我的错误,它没有逐个位置移动 - 它可以在列表中移动,但在释放时它会返回其 fromPosition 并且 notifyItemRangeChanged 强制它动态更改悬停项目的值,看起来有点糟糕。 尝试只添加 notifyitemmoved 并从我的回答中删除第二个和第三个通知电话 拥有一个 notifyItemMoved(fromPosition, toPosition) 只会通知正在被拖动的项目,必须有一个关于到达 fromPosition 的项目的通知(在我的情况下是 notifyItemChanged(fromPosition)。什么 notifyItemMoved你要我添加吗?当第一个通知添加 notifyItemMoved(toPosition, fromPosition) 时,它释放时不会改变任何东西,一切都会回到原来的位置。【参考方案2】:在我看来,您的 onItemMove() 似乎并没有考虑到所有的变化。 当某物移动不止一个项目时(假设在 A 和 B 之间),您正在交换两者之间的所有项目。 但是,您只向 A 和 B 报告更改,而不向其间的其他元素报告。
我建议你写一个交换方法来报告所有的变化:
public boolean swapItems(int fromPosition, int toPosition)
Collections.swap(list, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
notifyItemMoved(toPosition, fromPosition);
// And maybe also notifyItemChanged() as the item changes due to the shift
notifyItemChanged(fromPosition);
notifyItemChanged(toPosition);
调用此函数而不是 Collections.swap() 并删除其余的通知代码。
【讨论】:
遗憾的是,它不起作用,当使用此代码拖动一个项目时,所有项目都会在拖动时动态更改其值,这看起来很奇怪,当释放时,一个项目会回到原来的位置。 顺便说一句,像我以前做的那样做是不是正确的——只有 A 和 B 项目改变了它们的位置,那些在它们之间的留在原地?也许我应该通知 toPosition (+1 -1) 周围的那些项目,因为它们有时会干扰,我只是找不到它发生的原因(使用一些 if 语句 if toPosition == list.size(); )【参考方案3】:我知道它已经晚了,因为我看不到正确的答案在这里发布答案。只需在返回 true 之前添加notifyItemRangeChanged(fromPosition,toPosition)
。
notifyItemChanged(fromPosition);
notifyItemMoved(fromPosition, toPosition);
notifyItemRangeChanged(fromPosition,toPosition)
【讨论】:
以上是关于RecyclerView通过itemTouchHelper拖放快速拖动时很奇怪的主要内容,如果未能解决你的问题,请参考以下文章
Android列表拖动ListView~RecyclerView
Android列表拖动ListView~RecyclerView
Android 通过OnScrollListener来监听RecyclerView的位置
RecyclerView#onMeasure() 没有通过调用 setMeasuredDimension() 设置测量尺寸