recyclerView动画解读

来源:互联网 发布:常用理财软件 编辑:程序博客网 时间:2024/06/07 12:04

由于recyclerView有动画的情况会创建双倍的viewHolder,没有了解的,可以看看我的上一篇 为什么选择放弃recyclerView ,可是如果没有动画,item的变化太突兀了,那么这个需求就这样出来了,如何不用recyclerView的默认动画实现动画

继承ItemAnimator或者SimpleItemAnimator

我仿佛在逗我笑,就算继承了,还是要调用setAnimator方法,这样还是会创建viewHolder
那么,自己给view设定动画是否可以呢?
我们知道,recyclerView是通过notifyItem来改变item的状态的,这就是传说中的观察者模式,那么我们没有
notifyItemChanged,就算设置了view的动画,但是item没有改变,高度也不会变化。
少年, 你还年轻,跟着page继续解读。那么从最基础的开始

recyclerView中用到animator的地方有什么需要关注的地方

  1. 首先设置:setItemAnimator,这个似乎没什么看的
public void setItemAnimator(ItemAnimator animator) {        if (mItemAnimator != null) {            mItemAnimator.endAnimations();            mItemAnimator.setListener(null);        }        mItemAnimator = animator;        if (mItemAnimator != null) {            mItemAnimator.setListener(mItemAnimatorListener);        }    }

嗯,重新设置的时候把listener给取消
2. 接下来我们看看这个animator在哪里初始化的

    ItemAnimator mItemAnimator = new DefaultItemAnimator();

嗯,有一个默认的动画
3. 动画是在哪开启的呢?

    private Runnable mItemAnimatorRunner = new Runnable() {        @Override        public void run() {            if (mItemAnimator != null) {                mItemAnimator.runPendingAnimations();            }            mPostedAnimatorRunner = false;        }    };

交给runnable去执行

    private void postAnimationRunner() {        if (!mPostedAnimatorRunner && mIsAttached) {            ViewCompat.postOnAnimation(this, mItemAnimatorRunner);            mPostedAnimatorRunner = true;        }    }    private void animateAppearance(@NonNull ViewHolder itemHolder,            @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {        itemHolder.setIsRecyclable(false);        if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {            postAnimationRunner();        }    }    private void animateDisappearance(@NonNull ViewHolder holder,            @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {        addAnimatingView(holder);        holder.setIsRecyclable(false);        if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {            postAnimationRunner();        }    }    private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,            @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,            boolean oldHolderDisappearing, boolean newHolderDisappearing) {        oldHolder.setIsRecyclable(false);        if (oldHolderDisappearing) {            addAnimatingView(oldHolder);        }        if (oldHolder != newHolder) {            if (newHolderDisappearing) {                addAnimatingView(newHolder);            }            oldHolder.mShadowedHolder = newHolder;            // old holder should disappear after animation ends            addAnimatingView(oldHolder);            mRecycler.unscrapView(oldHolder);            newHolder.setIsRecyclable(false);            newHolder.mShadowingHolder = oldHolder;        }        if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {            postAnimationRunner();        }    }

有三个地方调用到了postAnimationRunner,要继续跟踪下去,略微复杂,page还无法做到三路线程同时运行,so,先缓一缓,我们先看看这个动画到底是做了什么的。

动画的内容

    // 清除    private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();    //  添加    private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();    // 移动    private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();    // 改变    private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();    private ArrayList<ArrayList<ViewHolder>> mAdditionsList = new ArrayList<>();    private ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();    private ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();    private ArrayList<ViewHolder> mAddAnimations = new ArrayList<>();    private ArrayList<ViewHolder> mMoveAnimations = new ArrayList<>();     private ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();    private ArrayList<ViewHolder> mChangeAnimations = new ArrayList<>();    private static class MoveInfo {        public ViewHolder holder;        public int fromX, fromY, toX, toY;        private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) {            this.holder = holder;            this.fromX = fromX;            this.fromY = fromY;            this.toX = toX;            this.toY = toY;        }    }    private static class ChangeInfo {        public ViewHolder oldHolder, newHolder;        public int fromX, fromY, toX, toY;        private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) {            this.oldHolder = oldHolder;            this.newHolder = newHolder;        }        private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder,                int fromX, int fromY, int toX, int toY) {            this(oldHolder, newHolder);            this.fromX = fromX;            this.fromY = fromY;            this.toX = toX;            this.toY = toY;        }    }

从这里可以看到,这个animator对所有的viewHolder都有一个缓存

    @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();            }        }    }

先移除,然后移动,再然后改变,最后添加。
在这些操作中,都可以看见循环的存在
我竟然在endAnimation中发现了一个todo
// TODO if some other animations are chained to end, how do we cancel them as well?

大致介绍完,我们具体来看看每个操作都做了啥
移除

        // First, remove stuff        for (ViewHolder holder : mPendingRemovals) {            animateRemoveImpl(holder);        }        mPendingRemovals.clear();    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();    }

只是一个简单的alpha 变为0,在动画结束后又设置alpha为1
这不相当于没有改变吗?少年,你还是太简单了,如果这个view不在这上面了,那设置alpha=1又有什么关系?当然这只是我的一个推断而已,具体是否是这样,之后再看。

这个animatorRemove方法是在animateDisappearance(simpleItemAnimator类)中调用的,而这个方法又是在这调用的

    private void animateDisappearance(@NonNull ViewHolder holder,            @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {        addAnimatingView(holder);        holder.setIsRecyclable(false);        if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {            postAnimationRunner();        }    }

这个方法是返回true的,

    @Override    public boolean animateRemove(final ViewHolder holder) {        resetAnimation(holder);        mPendingRemovals.add(holder);        return true;    }

也就是说,在动画开启前,先把viewHolder加入animator的缓存集合中,再开启动画

先到这里吧,国庆之后再继续

0 0
原创粉丝点击