RecyclerView的自定义项目动画师。幻灯片动画后折叠空间

时间:2015-08-13 05:10:39

标签: android android-recyclerview

我尝试根据这些sources

RecyclerView 编写自定义 ItemAnimator

所以,我有BaseItemAnimator

public abstract class BaseItemAnimator extends RecyclerView.ItemAnimator {
    protected List<RecyclerView.ViewHolder> pendingRemovals     = new ArrayList<>();
    protected List<RecyclerView.ViewHolder> pendingAdditions    = new ArrayList<>();
    protected List<MoveState> pendingMoves                      = new ArrayList<>();

    protected List<RecyclerView.ViewHolder> pendingRunAdditions = new ArrayList<>();
    protected List<MoveState> pendingRunMoves                   = new ArrayList<>();

    protected List<RecyclerView.ViewHolder> removals    = new ArrayList<>();
    protected List<RecyclerView.ViewHolder> additions   = new ArrayList<>();
    protected List<RecyclerView.ViewHolder> moves       = new ArrayList<>();

    protected static class MoveState {
        public RecyclerView.ViewHolder holder;
        public int fromX, fromY, toX, toY;

        /**
         * Constructor
         * @param holder ViewHolder
         * @param fromX Start x-coordinate of view location
         * @param fromY Start y-coordinate of view location
         * @param toX End x-coordinate of view location
         * @param toY End y-coordinate of view location
         */
        public MoveState(RecyclerView.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;
        }
    }

    @Override
    public void runPendingAnimations() {
        final boolean isEmptyRemovals     = pendingRemovals.isEmpty();
        final boolean isEmptyAdditions    = pendingAdditions.isEmpty();
        final boolean isEmptyMoves        = pendingMoves.isEmpty();

        if(isEmptyRemovals && isEmptyAdditions && isEmptyMoves)
            return; //Animation is not required

        //Execute remove animations
        for(RecyclerView.ViewHolder holder : pendingRemovals) {
            executeRemoveAnimation(holder);
        }

        pendingRemovals.clear();
        //Execute move animations
        if(!isEmptyMoves) {
            pendingRunMoves.addAll(pendingMoves);
            pendingMoves.clear();
            Runnable moveAnimationRunner = new Runnable() {
                @Override
                public void run() {

                    for(MoveState moveState : pendingRunMoves)
                        executeMoveAnimation(moveState);

                    pendingRunMoves.clear();
                }
            };

            if(!isEmptyMoves) {

            }
            else {
                moveAnimationRunner.run();
            }
        }

        //Execute add animations
        if(!isEmptyAdditions) {
            pendingRunAdditions.addAll(pendingAdditions);
            pendingAdditions.clear();
            Runnable addAnimationRunner = new Runnable() {

                @Override
                public void run() {

                    for(RecyclerView.ViewHolder holder : pendingRunAdditions)
                        executeAddAnimation(holder);

                    pendingRunAdditions.clear();
                }
            };

            if(!(isEmptyMoves || isEmptyRemovals)) {

            }
            else {
                addAnimationRunner.run();
            }
        }
    }

    @Override
    public boolean animateRemove(RecyclerView.ViewHolder holder) {
        prepareRemoveAnimation(holder);
        pendingRemovals.add(holder);
        return true;
    }

    @Override
    public boolean animateAdd(RecyclerView.ViewHolder holder) {
        prepareAddAnimation(holder);
        pendingAdditions.add(holder);
        return true;
    }

    @Override
    public boolean animateMove(RecyclerView.ViewHolder holder,
                               int fromX, int fromY, int toX, int toY) {
        final View view = holder.itemView;
        int dX = toX - fromX;
        int dY = toY - fromY;

        if (dX == 0 && dY == 0) {
            dispatchMoveFinished(holder);
            return false;
        }

        if (dX != 0) {
            ViewCompat.setTranslationX(view, -dX);
        }

        if (dY != 0) {
            ViewCompat.setTranslationY(view, -dY);
        }

        pendingMoves.add(new MoveState(holder, fromX, fromY, toX, toY));
        return true;
    }

    @Override
    public boolean animateChange(RecyclerView.ViewHolder oldHolder,
                                 RecyclerView.ViewHolder newHolder,
                                 int fromLeft, int fromTop,
                                 int toLeft, int toTop) {
        //TODO: Make change animation
        return false;
    }

    private void resetTranslation(final View view) {
        ViewCompat.setTranslationY(view, 0);
        ViewCompat.setTranslationX(view, 0);
    }

    @Override
    public void endAnimation(RecyclerView.ViewHolder item) {
        final View view = item.itemView;
        ViewCompat.animate(view).cancel();

        if (pendingMoves.remove(item)) {
            resetTranslation(view);
            dispatchMoveFinished(item);
        }

        if (pendingRemovals.remove(item)) {
            dispatchRemoveFinished(item);
        }

        if (pendingAdditions.remove(item)) {
            ViewCompat.setAlpha(view, 1);
            dispatchAddFinished(item);
        }

        if (moves.remove(item)) {
            resetTranslation(view);
            dispatchMoveFinished(item);
        }

        if (removals.remove(item)) {
            ViewCompat.setAlpha(view, 1);
            dispatchRemoveFinished(item);
        }

        if (additions.remove(item)) {
            ViewCompat.setAlpha(view, 1);
            dispatchAddFinished(item);
        }

        dispatchFinishedWhenDone();
    }

    @Override
    public void endAnimations() {
        int count = pendingMoves.size();

        for (int i = count - 1; i >= 0; i--) {
            MoveState item = pendingMoves.get(i);
            View view = item.holder.itemView;
            ViewCompat.animate(view).cancel();
            resetTranslation(view);
            dispatchMoveFinished(item.holder);
            pendingMoves.remove(item);
        }

        count = pendingRemovals.size();

        for (int i = count - 1; i >= 0; i--) {
            RecyclerView.ViewHolder item = pendingRemovals.get(i);
            dispatchRemoveFinished(item);
            pendingRemovals.remove(item);
        }

        count = pendingAdditions.size();

        for (int i = count - 1; i >= 0; i--) {
            RecyclerView.ViewHolder item = pendingAdditions.get(i);
            View view = item.itemView;
            ViewCompat.setAlpha(view, 1);
            dispatchAddFinished(item);
            pendingAdditions.remove(item);
        }

        if (!isRunning()) {
            return;
        }

        count = moves.size();

        for (int i = count - 1; i >= 0; i--) {
            RecyclerView.ViewHolder item = moves.get(i);
            View view = item.itemView;
            ViewCompat.animate(view).cancel();
            resetTranslation(view);
            dispatchMoveFinished(item);
            moves.remove(item);
        }

        count = removals.size();

        for (int i = count - 1; i >= 0; i--) {
            RecyclerView.ViewHolder item = removals.get(i);
            View view = item.itemView;
            ViewCompat.animate(view).cancel();
            ViewCompat.setAlpha(view, 1);
            dispatchRemoveFinished(item);
            removals.remove(item);
        }

        count = additions.size();

        for (int i = count - 1; i >= 0; i--) {
            RecyclerView.ViewHolder item = additions.get(i);
            View view = item.itemView;
            ViewCompat.animate(view).cancel();
            ViewCompat.setAlpha(view, 1);
            dispatchAddFinished(item);
            additions.remove(item);
        }

        pendingRunMoves.clear();
        pendingRunAdditions.clear();
        dispatchAnimationsFinished();
    }

    @Override
    public boolean isRunning() {
        return !(moves.isEmpty()
                && removals.isEmpty()
                && additions.isEmpty()
                && pendingRunMoves.isEmpty()
                && pendingRunAdditions.isEmpty());
    }

    protected abstract void prepareAddAnimation(RecyclerView.ViewHolder holder);

    protected abstract void prepareRemoveAnimation(RecyclerView.ViewHolder holder);

    protected abstract void executeAddAnimation(RecyclerView.ViewHolder holder);

    protected abstract void executeRemoveAnimation(RecyclerView.ViewHolder holder);

    protected void executeMoveAnimation(final MoveState moveState) {
        final View view = moveState.holder.itemView;
        final int dX = moveState.toX - moveState.fromX;
        final int dY = moveState.toY - moveState.fromY;
        ViewCompat.animate(view).cancel();

        if (dX != 0) {
            ViewCompat.animate(view).translationX(0);
        }

        if (dY != 0) {
            ViewCompat.animate(view).translationY(0);
        }

        // TODO: make EndActions end listeners instead, since end actions aren't called when
        // vpas are canceled (and can't end them. why?)
        // need listener functionality in VPACompat for this. Ick.
        ViewCompat.animate(view)
                .setDuration(getMoveDuration())
                .setListener(new DefaultVPAListener() {

                    @Override
                    public void onAnimationCancel(View view) {

                        if (dX != 0) {
                            ViewCompat.setTranslationX(view, 0);
                        }

                        if (dY != 0) {
                            ViewCompat.setTranslationY(view, 0);
                        }
                    }

                    @Override
                    public void onAnimationEnd(View view) {
                        dispatchMoveFinished(moveState.holder);
                        moves.remove(moveState.holder);
                        dispatchFinishedWhenDone();
                    }

                }).start();

        moves.add(moveState.holder);
    }

    /**
     * Check the state of currently pending and running animations. If there are none
     * pending/running, call {@link #dispatchAnimationsFinished()} to notify any
     * listeners.
     */
    protected void dispatchFinishedWhenDone() {

        if (!isRunning()) {
            dispatchAnimationsFinished();
        }
    }

    public static class DefaultVPAListener implements ViewPropertyAnimatorListener {

        @Override
        public void onAnimationStart(View view) {

        }

        @Override
        public void onAnimationEnd(View view) {

        }

        @Override
        public void onAnimationCancel(View view) {

        }
    }
}

和ColorSlideItemAnimator

public class ColorSlideItemAnimator extends BaseItemAnimator {
    protected RecyclerView recyclerView;

    public ColorSlideItemAnimator(RecyclerView recyclerView) {
        this.recyclerView = recyclerView;
    }

    @Override
    protected void prepareAddAnimation(final RecyclerView.ViewHolder holder) {
        ViewCompat.setTranslationX(holder.itemView, -recyclerView.getWidth());
    }

    @Override
    protected void prepareRemoveAnimation(RecyclerView.ViewHolder holder) {

    }

    @Override
    protected void executeAddAnimation(final RecyclerView.ViewHolder holder) {
        final View view = holder.itemView;
        ViewCompat.animate(view).cancel();
        ViewCompat.animate(view)
                .translationX(0)
                .setDuration(getAddDuration())
                .setListener(new DefaultVPAListener() {

                    @Override
                    public void onAnimationCancel(View view) {
                        ViewCompat.setTranslationX(view, 0);
                    }

                    @Override
                    public void onAnimationEnd(View view) {
                        dispatchAddFinished(holder);
                        additions.remove(holder);
                        dispatchFinishedWhenDone();
                    }

                }).start();

        additions.add(holder);
    }

    @Override
    protected void executeRemoveAnimation(final RecyclerView.ViewHolder holder) {
        final View view = holder.itemView;
        ViewCompat.animate(view).cancel();
        ViewCompat.animate(view)
                .setDuration(getRemoveDuration())
                .translationX(-recyclerView.getWidth())
                .setListener(new DefaultVPAListener() {

                    @Override
                    public void onAnimationEnd(View view) {
                        ViewCompat.setTranslationX(view, -recyclerView.getWidth());
                        dispatchRemoveFinished(holder);
                        removals.remove(holder);
                        dispatchFinishedWhenDone();
                    }

                }).start();

        removals.add(holder);
    }
}

我编写此代码以了解git上的源代码,我可以编写需要我的动画师。这个代码有效,它是删除事件的幻灯片项目,但我无法理解当项目删除时如何折叠空间?

现在,项目滑动后我有空格。

P.S。我很抱歉我的英语。如果某些事情不可理解,我会尝试解释更多细节。

0 个答案:

没有答案