RecyclerView.ItemAnimator终极解读(二)--SimpleItemAnimator和DefaultItemAnimator源码解析

这一章节主要来了解SimpleItemAnimator和DefaultItemAnimator这两个类

先来看一下SimpleItemAnimator,直接上图


上图是SDK中SimpleItemAnimator的继承关系,可以看到它直接继承自ItemAnimator类,注释信息主要的意思就是它是对ItemAnimator的一个封装类,通过记录View的边界信息来决定需要执行的move、change、add还是remove等动画效果。并且这个类也复用了ItemAnimator中的一些方法,比如它还是使用ItemHolderInfo来保存某一View的信息,具体不了解的可以查看我的上一篇博客 RecyclerView.ItemAnimator终极解读(一)--RecyclerView源码解析 。你可以复写obtainHolderInfo()方法来返回你自定义的ItemHolderInfo信息。


在上一篇博客的最后,我们已经知道,最终就是调用ItemAnimator的animateChange、animateAppearance、runPendingAnimations等方法来显示和执行动画效果,那SimpleItemAnimator对这几个方法是如何封装的呢?

还是以添加一个Item举例,最后调用的是animateAppearance方法,SimpleItemAnimator的animateAppearance方法代码如下:

@Override
    public boolean animateAppearance(@NonNull ViewHolder viewHolder,
            @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
        if (preLayoutInfo != null && (preLayoutInfo.left != postLayoutInfo.left
                || preLayoutInfo.top != postLayoutInfo.top)) {
            // slide items in if before/after locations differ
            if (DEBUG) {
                Log.d(TAG, "APPEARING: " + viewHolder + " with view " + viewHolder);
            }
            return animateMove(viewHolder, preLayoutInfo.left, preLayoutInfo.top,
                    postLayoutInfo.left, postLayoutInfo.top);
        } else {
            if (DEBUG) {
                Log.d(TAG, "ADDED: " + viewHolder + " with view " + viewHolder);
            }
            return animateAdd(viewHolder);
        }
}
其实封装的很简单,只是判断了一下布局前后两个ViewHolder的left/top坐标是否相等 如果不相等则调用animateMove,否则调用animateAdd(viewHolder)方法。

整理一下其实就是给animateAppearance改了一下名字而已,改成animateAdd方法更加见名知意罢了

animateAdd(viewHolder)是一个抽象方法,需要子类去实现。

注意:SimpleItemAnimator并没有实现runPendingAnimations方法,因此如果继承自SimpleItemAnimator来实现自定义动画的话,还是需要自己书写执行动画的代码


接下来看一下DefaultItemAnimator,它是Android SDK中提供的一个默认动画实现类,如果产品并没有复杂的动画需求,可以直接使用这个API,实现简单的动画效果

可以看到DefaultItemAnimator继承自SimpleItemAnimator,因此如果想实现添加一个Item的动画效果,只需要实现两个方法,在animateAdd方法中将ViewHolder添加到集合中,

然后在runPendingAnimations方法中执行所有的add动画即可, 如以下代码所示:

animateAdd

@Override
    public boolean animateAdd(final ViewHolder holder) {
        resetAnimation(holder);
        ViewCompat.setAlpha(holder.itemView, 0);
        mPendingAdditions.add(holder);
        return true;
}

首先调用resetAnimation(holder)停止此holder中itemView的动画效果,然后将holder.itemView的透明度设置为0不可见状态。

将holder添加到mPendingAdditions集合中,注意:此集合一会儿会在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) {
            // nothing to animate
            return;
        }
        // First, remove stuff
        for (ViewHolder holder : mPendingRemovals) {
            animateRemoveImpl(holder);
        }
        mPendingRemovals.clear();
        // Next, move stuff
        if (movesPending) {
            final ArrayList<MoveInfo> moves = new ArrayList<>();
            moves.addAll(mPendingMoves);
            mMovesList.add(moves);
            mPendingMoves.clear();
            Runnable mover = new Runnable() {
                @Override
                public void run() {
                    for (MoveInfo moveInfo : moves) {
                        animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
                                moveInfo.toX, moveInfo.toY);
                    }
                    moves.clear();
                    mMovesList.remove(moves);
                }
            };
            if (removalsPending) {
                View view = moves.get(0).holder.itemView;
                ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
            } else {
                mover.run();
            }
        }
        // Next, change stuff, to run in parallel with move animations
        if (changesPending) {
            final ArrayList<ChangeInfo> changes = new ArrayList<>();
            changes.addAll(mPendingChanges);
            mChangesList.add(changes);
            mPendingChanges.clear();
            Runnable changer = new Runnable() {
                @Override
                public void run() {
                    for (ChangeInfo change : changes) {
                        animateChangeImpl(change);
                    }
                    changes.clear();
                    mChangesList.remove(changes);
                }
            };
            if (removalsPending) {
                ViewHolder holder = changes.get(0).oldHolder;
                ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
            } else {
                changer.run();
            }
        }
        // Next, add stuff
        if (additionsPending) {
            final ArrayList<ViewHolder> additions = new ArrayList<>();
            additions.addAll(mPendingAdditions);
            mAdditionsList.add(additions);
            mPendingAdditions.clear();
            Runnable adder = new Runnable() {
                public void run() {
                    for (ViewHolder holder : additions) {
                        animateAddImpl(holder);
                    }
                    additions.clear();
                    mAdditionsList.remove(additions);
                }
            };
            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();
            }
        }
}
12行~14行 remove最先执行,for循环遍历mPendingRemovals集合中所有的ViewHolder,然后调用animateRemoveImpl(holder)执行删除动画,代码如下所示:

private void animateRemoveImpl(final ViewHolder holder) {
        final View view = holder.itemView;
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
        mRemoveAnimations.add(holder);
        animation.setDuration(getRemoveDuration())
                .alpha(0).setListener(new VpaListenerAdapter() {
            @Override
            public void onAnimationStart(View view) {
                dispatchRemoveStarting(holder);
            }

            @Override
            public void onAnimationEnd(View view) {
                animation.setListener(null);
                ViewCompat.setAlpha(view, 1);
                dispatchRemoveFinished(holder);
                mRemoveAnimations.remove(holder);
                dispatchFinishedWhenDone();
            }
        }).start();
}

17行~62行:remove执行完之后,再同事开始move和change动画,

move和change执行完之后,最后执行add动画,调用的是animateAddImpl(holder)方法,代码如下所示:

private void animateAddImpl(final ViewHolder holder) {
        final View view = holder.itemView;
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
        mAddAnimations.add(holder);
        animation.alpha(1).setDuration(getAddDuration()).
                setListener(new VpaListenerAdapter() {
                    @Override
                    public void onAnimationStart(View view) {
                        dispatchAddStarting(holder);
                    }
                    @Override
                    public void onAnimationCancel(View view) {
                        ViewCompat.setAlpha(view, 1);
                    }

                    @Override
                    public void onAnimationEnd(View view) {
                        animation.setListener(null);
                        dispatchAddFinished(holder);
                        mAddAnimations.remove(holder);
                        dispatchFinishedWhenDone();
                    }
                }).start();
}
可以看出默认的DefaultItemAnimator添加item动画实际上就是一个透明度的变化效果。

注意:在DefaultItemAnimator中的mPendingRemovals和mPendingAdditions都是私有的,因此如果要继承DefaultItemAnimator实现自定义动画的话,无法通过在animateAdd方法中向此集合添加ViewHolder来实现。只能复写animateAdd、animateChange等方法,在这些方法中主动调用Animation.start方法执行动画类


后续两节会依次介绍通过继承SimpleItemAnimator和DefaultItemAnimator来实现自定义Item动画



  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
RecyclerView.Adapter 是一个用于管理 RecyclerView 中数据和视图的基类。它有三个主要的方法:onCreateViewHolder、onBindViewHolder 和 getItemCount。 onCreateViewHolder 方法用于创建 ViewHolder 对象,ViewHolder 对象用于保存 RecyclerView 中的视图。onBindViewHolder 方法用于将数据绑定到 ViewHolder 中的视图上。getItemCount 方法用于返回 RecyclerView 中的数据项数量。 在实现 RecyclerView.Adapter 时,我们需要重写这三个方法,并根据实际需求进行相应的处理。此外,我们还可以添加一些其他的方法,例如添加、删除、更新数据等。 下面是一个简单的 RecyclerView.Adapter 实现示例: ``` public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private List<String> mData; public MyAdapter(List<String> data) { mData = data; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false); ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { String data = mData.get(position); holder.mTextView.setText(data); } @Override public int getItemCount() { return mData.size(); } public static class ViewHolder extends RecyclerView.ViewHolder { public TextView mTextView; public ViewHolder(View itemView) { super(itemView); mTextView = (TextView) itemView.findViewById(R.id.text_view); } } } ``` 在这个示例中,我们创建了一个 MyAdapter 类,它继承自 RecyclerView.Adapter。在构造函数中,我们传入了一个数据列表 mData。在 onCreateViewHolder 方法中,我们使用 LayoutInflater 创建了一个视图,并将其封装在 ViewHolder 中返回。在 onBindViewHolder 方法中,我们将数据绑定到 ViewHolder 中的视图上。在 getItemCount 方法中,我们返回 mData 的大小。 这是一个简单的 RecyclerView.Adapter 实现示例,实际应用中可能需要更复杂的处理逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值