RecyclerView ItemTouchHelper轻扫移除animation

我有一个删除刷卡,绘制一个背景(很像收件箱应用程序),由一个ItemTouchHelper实现 – 通过重写onChilDraw方法,并在提供的canvas上绘制一个矩形:

ItemTouchHelper mIth = new ItemTouchHelper( new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) { public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { remove(viewHolder.getAdapterPosition()); } public boolean onMove(RecyclerView recyclerview, RecyclerView.ViewHolder v, RecyclerView.ViewHolder target) { return false; } @Override public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { View itemView = viewHolder.itemView; Drawable d = ContextCompat.getDrawable(context, R.drawable.bg_swipe_item_right); d.setBounds(itemView.getLeft(), itemView.getTop(), (int) dX, itemView.getBottom()); d.draw(c); super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } }); 

上面调用的remove方法在Adapter中:

  public void remove(int position) { items.remove(position); notifyItemRemoved(position); } 

背景绘制得很好,但是当notifyItemRemoved被调用时(根据先生debugging器),RecyclerView首先删除我漂亮的绿色背景,然后推动两个相邻的项目在一起。

在这里输入图像说明在这里输入图像说明

我希望它保持背景,而这样做(就像收件箱应用程序)。 有没有办法做到这一点?

Solutions Collecting From Web of "RecyclerView ItemTouchHelper轻扫移除animation"

我有同样的问题,我不想引入一个新的库,只是为了解决这个问题。 RecyclerView并没有删除你漂亮的绿色背景,它只是重绘自己,而你的ItemTouchHelper不再是绘图了。 其实它是绘图,但是dX是0,并从itemView.getLeft() (它是0)绘制到dX (这是0),所以你什么都看不到。 而且画得太多了,但我稍后再回来。

无论如何回到背景,而行animation:我不能在ItemTouchHelperonChildDraw 。 最后,我不得不添加另一个项目装饰器来做到这一点。 它沿着这些线路:

 public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { if (parent.getItemAnimator().isRunning()) { // find first child with translationY > 0 // draw from it's top to translationY whatever you want int top = 0; int bottom = 0; int childCount = parent.getLayoutManager().getChildCount(); for (int i = 0; i < childCount; i++) { View child = parent.getLayoutManager().getChildAt(i); if (child.getTranslationY() != 0) { top = child.getTop(); bottom = top + (int) child.getTranslationY(); break; } } // draw whatever you want super.onDraw(c, parent, state); } } 

这段代码只考虑animation的行,但你也应该考虑行下降。 如果你滑动删除最后一行,会发生这种情况,上面的行将animation到该空间。

当我说你的ItemTouchHelper绘制太多我的意思是:看起来像ItemTouchHelper保持ViewHolder删除行的情况下,以防他们需要恢复。 除了要刷的VH以外,还要求对那些VH进行ChildDraw。 不知道这种行为的内存pipe理影响,但我需要在onChildDraw的开始额外的检查,以避免绘制“fantom”行。

 if (viewHolder.getAdapterPosition() == -1) { return; } 

在你的情况下,它是从左= 0到右= 0,所以你什么都看不到,只是有开销。 如果你开始看到之前刷过的背景,那就是原因。

编辑:我已经去了这里,看到这个博客文章和这个github回购 。

我设法通过使用Wasabeefs的recyclerview-animators库来实现它。

我的ViewHolder现在扩展了库提供的AnimateViewHolder:

  class MyViewHolder extends AnimateViewHolder { TextView textView; public MyViewHolder(View itemView) { super(itemView); this.textView = (TextView) itemView.findViewById(R.id.item_name); } @Override public void animateAddImpl(ViewPropertyAnimatorListener listener) { ViewCompat.animate(itemView) .translationY(0) .alpha(1) .setDuration(300) .setListener(listener) .start(); } @Override public void preAnimateAddImpl() { ViewCompat.setTranslationY(itemView, -itemView.getHeight() * 0.3f); ViewCompat.setAlpha(itemView, 0); } @Override public void animateRemoveImpl(ViewPropertyAnimatorListener listener) { ViewCompat.animate(itemView) .translationY(0) .alpha(1) .setDuration(300) .setListener(listener) .start(); } } 

被忽略的函数实现与recyclerview-animators在github上的自述文件中的内容完全相同。

也似乎有必要改变ItemAnimator到一个自定义的,并设置removeDuration为0(或另一个低值 – 这是为了防止一些闪烁):

  recyclerView.setItemAnimator(new SlideInLeftAnimator()); recyclerView.getItemAnimator().setRemoveDuration(0); 

这不会导致任何问题,因为使用的正常(非滑动)移除animation是AnimateViewHolder中的移动animation。

所有其他代码保持与问题中相同。 我还没有时间弄清楚这个问题的内部工作原理,但如果有人觉得可以随时更新这个答案。

更新:设置recyclerView.getItemAnimator().setRemoveDuration(0); 实际上打破了重击的“重新绑定”animation。 幸运的是,在animateRemoveImpl(500为我工作)中删除该行并设置更长的持续时间也解决了闪烁问题。

更新2:原来,ItemTouchHelper.SimpleCallback使用ItemAnimator的animation持续时间,这就是为什么上述setRemoveDuration(0)打破了滑动animation。 简单地重写它的方法getAnimationDuration来:

 @Override public long getAnimationDuration(RecyclerView recyclerView, int animationType, float animateDx, float animateDy) { return animationType == ItemTouchHelper.ANIMATION_TYPE_DRAG ? DEFAULT_DRAG_ANIMATION_DURATION : DEFAULT_SWIPE_ANIMATION_DURATION; } 

解决了这个问题。