我们是在使用Recyclerview的时候,往往会想着搞一些比较用户交互体验比较好的效果,拖拽或者删除等等。

在android5.0之后android v7包下提供了ItemTouchHelper,供我们开发者实现这些效果。

这里就不再详细赘述itemTouchHelper如何使用了,网上有很多好的文章介绍,

这里就推荐一篇https://www.jianshu.com/p/e3426dcc8ef1,作者GitLqr。写的很不错。

我主要是在对于ItemTouchHelper使用过程中遇到的几个问题记录一下。

第一个问题:

我们在拖拽item交换位置后,再次点击item获得对应的position的数据不正确。

第二个问题:

是根据修复第一问题后产生的,具体问题是拖拽后重新刷新position后,会再次刷新

被拖拽item与之交互的item,会产生刷新动画的效果。

不知道如何插入视频,本来是录了一个,查了很多资料却貌似csdn现在不支持了。

给一个视频链接吧http://www.56.com/u82/v_MTU5ODIzOTEx.html。我录了一下放到这上面了。

从视频中我们看到这种刷新效果很不友好。

 

 

对于上述两个问题我们来一一解决。

对于第一个问题,首先看代码:

  1. //一个拖拽接口
  2. public interface ItemMoveListener {
  3. boolean onItemMove(int fromPosition,int toPosition);
  4. }
  5. 然后在ItemTouchHelper.CallBack的回调方法onMove中调用
  6. /**
  7. * 当item拖拽移动时触发
  8. * @param recyclerView
  9. * @param viewHolder
  10. * @param viewHolder1
  11. * @return
  12. */
  13. @Override
  14. public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
  15. return mItemMoveListener.onItemMove(viewHolder.getAdapterPosition(),viewHolder1.getAdapterPosition());
  16. }
  17. 然后在我们的adapter中实现onItemMove
  18. @Override
  19. public boolean onItemMove(int fromPosition, int toPosition) {
  20. Collections.swap(list,fromPosition,toPosition);
  21. notifyItemMoved(fromPosition,toPosition);
  22. return true;
  23. }

我们在实现类中onItemMove方法中调用

Collections.swap(list,fromPosition,toPosition); 更改list中开始和结尾position的位置

notifyItemMoved(fromPosition,toPosition);刷新从开始到结尾position 的位置。

到这里没有毛病。但是我们可以在拖拽后,然后点击拖拽后的item,在Logcat控制台打印一下所点击item对应的数据,你会发现

已经不正确的,这是因为你在拖拽的时候,每当拖拽的item被拖拽到最邻近的item旁边时,就会和邻近的item触发onMove方法,进而调用了onItemMove方法,进行了position相互。然后再拿着互换后的position(被拖拽的item)再与下一个即将邻近的item的position进行互换。如图

不能上传视频是一大遗憾,若是知道如何上传,请老铁们留个言,指导一下。

我们拖拽后点击事实上position的值已经与list中变化后的position不对应了,所以在获得item中的数据的时候list.get(position).xx已经不正确了。

从图中我们可以看到,事实上item(position)总是与之最近的item进行位置交换,随之position进行交换。

那么我们要做就是两种要么就是对这两个交换的item交换后再把position的位置交换回来,第二种就是记录拖拽的item的positon和最后一个被交换的item 的positon,然后把这两个item之间(包含这个两个)的item依次再刷新交换后重新排列后的position的值。

显然第一种操作的频率很高(若是拖拽之后,经过的item很多),而且实践证明并不能把正确的positon展现出来。

第二种是我用的。

我是先记录一下拖拽item的positon的值firstPositon和最后一个被交换item的positon的值endPosition。

  1. @Override
  2. public boolean onItemMove(int fromPosition, int toPosition) {
  3. if(isFirst){
  4. firstPosition=fromPosition;
  5. isFirst=false;
  6. }
  7. endPosition=toPosition;
  8. Collections.swap(list,fromPosition,toPosition);
  9. notifyItemMoved(fromPosition,toPosition);
  10. return true;
  11. }

然后写了一个监听回调,在拖拽动作完成的时候调用该回调方法

  1. /**
  2. * 当item的交互动画结束时触发
  3. * @param recyclerView
  4. * @param viewHolder
  5. */
  6. @Override
  7. public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
  8. super.clearView(recyclerView, viewHolder);
  9. viewHolder.itemView.setBackgroundColor(viewHolder.itemView.getContext().getResources().getC
  10. mIteClearListener.itemClearViewAction();
  11. }

然后在adpter中实现该接口。

  1. @Override
  2. public void itemClearViewAction() {
  3. isFirst=true;//释放状态
  4. notifyItemRangeChanged(Math.min(firstPosition, endPosition), Math.abs(firstPosition - endPosition) +1);
  5. }

这里的

notifyItemRangeChanged(int positionStart, int itemCount)Android已经很好的提供给我们了,使用即可。

ok,到这里第一个问题就解决了。

看第二个问题,也是在第一个问题的基础上产生的。上面有录的视频地址,里面可以清晰的看到调用notifyItemRangeChanged后

被刷新到的item会都会有一个刷新的效果,很很友好,一般我们想要的结果是拖拽item刷新一下即可,提示用户,你拖拽的item被你拖拽到这里了。

在这里既然是刷新的效果,必然想到是recyclerview自身的,于是我就问了度娘,

recycler.getItemAnimator().setChangeDuration(0);

找到了这个方法,使刷新的动效的时间我设置为0。

于是运行了,发现很完美的解决了我的问题,而且动效只会在拖拽的item中上面有效果,这更是我想要的,但是我没有找到为何最后一个会有刷新的效果,按上面的设置,应该是所以的刷新效果都没有才对,却不知为何,这里埋下了伏笔,今晚估计睡不着了。有知道的伙伴,可以留言指导一下。