首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >RecyclerView ItemTouchHelper卷帘移除动画

RecyclerView ItemTouchHelper卷帘移除动画
EN

Stack Overflow用户
提问于 2015-07-15 07:07:56
回答 3查看 15.7K关注 0票数 16

我删除了swipe,它通过重写onChilDraw方法并在提供的画布上绘制一个矩形来绘制背景(很像收件箱应用程序),由ItemTouchHelper实现:

代码语言:javascript
复制
    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中:

代码语言:javascript
复制
    public void remove(int position) {
       items.remove(position);
       notifyItemRemoved(position);
    }

背景绘制得很好,但是当调用notifyItemRemoved时(根据Debugger先生的说法),RecyclerView首先删除我漂亮的绿色背景,然后将两个相邻的项推到一起。

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-01-09 09:02:23

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

无论如何,回到后台,同时行动画:我不能在ItemTouchHelperonChildDraw中做到这一点。最后,我不得不添加另一个项目装饰器来做这件事。它遵循这样的思路:

代码语言:javascript
复制
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);
    }
}

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

当我说你的ItemTouchHelper绘制的太多时,我的意思是:看起来ItemTouchHelper保留了删除行的ViewHolder,以防需要恢复它们。除了VH被刷之外,它还会为这些VH调用onChildDraw。不确定此行为对内存管理的影响,但我需要在onChildDraw的开头进行额外的检查,以避免绘制“幻象”行。

代码语言:javascript
复制
if (viewHolder.getAdapterPosition() == -1) {
    return;
}

在您的例子中,它是从left=0绘制到right=0,所以除了开销之外,您什么也看不到。如果你开始看到之前被刷走的行,绘制它们的背景,这就是原因。

编辑:我试过了,看看这个blog post和这个github repo

票数 5
EN

Stack Overflow用户

发布于 2015-07-15 23:37:39

我通过使用Wasabeefs's recyclerview-animators库设法让它正常工作。

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

代码语言:javascript
复制
    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();
    }

}

被覆盖的函数实现与github上的recyclerview animators自述文件中的内容相同。

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

代码语言:javascript
复制
    recyclerView.setItemAnimator(new SlideInLeftAnimator());
    recyclerView.getItemAnimator().setRemoveDuration(0);

这不会造成任何问题,因为使用的普通(非滑动)移除动画是AnimateViewHolder中的动画。

所有其他代码都与问题中的相同。我还没有时间弄清楚它的内部工作原理,但是如果有人想要这样做,请随时更新这个答案。

更新:设置recyclerView.getItemAnimator().setRemoveDuration(0);实际上会破坏swipe的“重新绑定”动画。幸运的是,删除这一行并在animateRemoveImpl中设置更长的持续时间(500对我来说可以)也可以解决闪烁问题。

更新2:原来ItemTouchHelper.SimpleCallback使用ItemAnimator的动画持续时间,这就是为什么上面的setRemoveDuration(0)破坏了卷帘动画。只需将其方法getAnimationDuration重写为:

代码语言:javascript
复制
@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;
}

解决了这个问题。

票数 1
EN

Stack Overflow用户

发布于 2018-06-08 06:46:01

只需更新适配器位置,然后删除动画

代码语言:javascript
复制
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
     int position = viewHolder.getAdapterPosition();

     if (direction == ItemTouchHelper.LEFT) {
          remove(position);
     } else {
          mAdapter.notifyItemChanged(position);
     }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31418905

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档