自定义recycleView的删除&移动的动画

本文介绍了在项目开发中如何自定义RecyclerView的删除和移动动画。针对UI的特定需求,提出了两种思路:直接在ViewHolder中设置动画或重写RecycleView的SimpleItemAnimator。在遇到需要同步执行删除和移动动画的场景时,由于RecyclerView默认行为的限制,选择重写SimpleItemAnimator的animateRemove()方法来实现预期效果。文章还分享了源码分析和简化重写过程的技巧。

首先 RecycleView是有一套自己默认的动画、以及相应执行时机
但是在项目开发中,遇到了需要为删除item的动效进行调整
思路1. 可以在holder中(比如点删除的点击事件)直接为当前view设置自定义动画并执行
思路2. 重写recycleView默认动画 SimpleItemAnimator

简单的场景下思路1是可以达到预期效果的,但是个人在项目中遇到了另一个难题,ui给的动画需要在执行删除动画的同时 下方列表要同时上移。

但是recycleView默认的是删除动画执行完之后才去执行移动动画 这种情况下思路1就无法达到预期 只能重写SimpleItemAnimator了

好消息是recycleView自身已经封装好了一个默认DefaultItemAnimator该类中对抽象类SimpleItemAnimator所有方法都以重写实现 我们只要继承DefaultItemAnimator对需要调整的方法重写即可
坏消息是 内部没有能直接提供调用开始移动动画或删除动画的方法 有的是runPendingAnimations() 该方法内部设置了各个动画的执行时机,我们重写这个方法 几乎等同于需要重写整个类
源码如下

  @Override
    public void runPendingAnimations() {
        boolean removalsPending = !mPendingRemovals.isEmpty();
        boolean movesPending = !mPendingMoves.isEmpty();
        boolean changesPending = !mPendingChanges.isEmpty();
        boolean additionsPending = !mPendingAdditions.isEmpty();
        // 判断是否有动画需要执行
        if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
            return;
        }
        // 执行Remove动画
        for (RecyclerView.ViewHolder holder : mPendingRemovals) {
            animateRemoveImpl(holder); // 实际执行Remove动画的方法
        }
        mPendingRemovals.clear();
        // Move动画
        if (movesPending) {
            // 注意: Move动画并不是马上执行,会放入一个Runnable
            Runnable mover = new Runnable() {
                @Override
                public void run() {
                    for (MoveInfo moveInfo : moves) {
                        animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
                                moveInfo.toX, moveInfo.toY);
                    }
                }
            };
            // 如果有Remove动画,就在Remove动画结束之后执行Move动画
            // 如果没有Remove动画就马上执行
            if (removalsPending) {
                View view = moves.get(0).holder.itemView;
                ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
            } else {
                mover.run();
            }
        }
        // Change动画,与Move动画一起执行
        if (changesPending) {
            // ......
            Runnable changer = new Runnable() {
                @Override
                public void run() {
                    for (ChangeInfo change : changes) {
                        animateChangeImpl(change);
                    }
                }
            };
            if (removalsPending) {
                RecyclerView.ViewHolder holder = changes.get(0).oldHolder;
                ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
            } else {
                changer.run();
            }
        }
        // 最后执行Add动画
        if (additionsPending) {
            // ......
            Runnable adder = new Runnable() {
                @Override
                public void run() {
                    for (RecyclerView.ViewHolder holder : additions) {
                        animateAddImpl(holder);
                    }
                    additions.clear();
                    mAdditionsList.remove(additions);
                }
            };
            // 在Remove、Move、Change动画都完成之后开始执行Add动画
            if (removalsPending || movesPending || changesPending) {
                long removeDuration = removalsPending ? getRemoveDuration() : 0;
                long moveDuration = movesPending ? getMoveDuration() : 0;
                long changeDuration = changesPending ? getChangeDuration() : 0;
                long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
                View view = additions.get(0).itemView;
                ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
            } else {
                adder.run();
            }
        }
    }

通过源码可以发现 我们要达到目标 只需要将 mover.run();执行的判断条件去掉即可 但是却要为此重写大量内容。

而从写删除数据的animateRemove()方法就要简便许多:
只需要为holder内的itemview增加动画即可

 @Override
    public boolean animateRemove(RecyclerView.ViewHolder holder) {
        Animation animation = null;
        // 增加item消失动画
        switch (itemId) {
            case R.id.rv_list:
                animation = AnimationUtils.loadAnimation(holder.itemView.getContext(), R.anim.move_animation);
                holder.itemView.setAnimation(animation);
                break;
            case R.id.rv_list_v2:
                animation = AnimationUtils.loadAnimation(holder.itemView.getContext(), R.anim.move_animation_v2);
                holder.itemView.setAnimation(animation);
                break;
            default:
                break;
        }
        
        // 原父类方法内容
        resetAnimation(holder);
        mPendingRemovals.add(holder);
        return true;
    }

最后则是外部设置:

recycleView.setItemAnimator(new RecycleAnimation());
// 设置删除动画执行时长 单位ms
recycleView.getItemAnimator().setRemoveDuration(333);
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值