Android RecyclerView 詳解(三) RecyclerView的動畫實現(移除、添加、改變、移動)和自定義動畫的實現

Android RecyclerView 詳解(三) RecyclerView的動畫實現(移除、添加、改變、移動)


一丶添加刪除時候的刷新問題
先上一下效果圖吧

1.爲了方便起見我們還是先添加三個按鈕分別實現添加刪除和改變

2.在Adapter中寫調用方法並進行刷新
 public void remove(int position){
        list.remove(position);
        notifyItemRemoved(position);
    }
    public void add(int position,String data) {
        list.add(position,data);
        notifyItemInserted(position);
    }
    public void change(int position,String data) {
        list.remove(position);
        list.add(position,data);
        notifyItemChanged(position);
    }
可以發現他和ListView的刷新不同的是,對應不同的刪除添加或者改變對應的notify都是不同的,如果不這樣的話那麼添加刪除改變的動畫將沒有。

3.在Mainactivity中調用方法就好
 case R.id.btn_add:
                myAdapter.add(0,"新加數據");
                myRecyclerView.scrollToPosition(0);
                break;
            case R.id.btn_delete:
                myAdapter.remove(0);
                break;
            case R.id.btn_change:
                myAdapter.change(0,"改變的數據");
                break;
注意!!!可以發現增加的時候多了一個方法,那麼這個方法是定位顯示的條目位置的,當你新添加方法的時候他不會顯示出來,需要將條目位置進行設定。


二、對原動畫進行更改達到自己想要的動畫

1.如何設置原動畫
//設置默認動畫
        DefaultItemAnimator animator = new DefaultItemAnimator();
        //設置動畫時間
        animator.setAddDuration(2000);
        animator.setRemoveDuration(2000);
        myRecyclerView.setItemAnimator(animator);
2.設置自己自定義的動畫

(1)首先這些動畫的方法都是私有的所以我們沒有辦法去重寫他,那麼我們可以新建一個屬於自己的動畫類來實現自定義動畫
(2)自定義動畫的實現(依然爲了方便起見我們添加四個按鈕來動態的設置自定義動畫方便對比)
a.首先我們需要建一個自己的動畫類將原本的那個代碼全部複製過來
b.刪除動畫(滑動消失)
先來效果圖吧
源碼
    @Override
    public boolean animateRemove(final ViewHolder holder) {
        resetAnimation(holder);
        mPendingRemovals.add(holder);
        return true;
    }
這段代碼是將移除動畫添加到集合當中

真正執行移除的動畫代碼如下(源碼)
private void animateRemoveImpl(final ViewHolder holder) {
        final View view = holder.itemView;//首先得到ItemView
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);//開啓一個屬性動畫
        mRemoveAnimations.add(holder);//加入到要刪除的VIew中
/*        animation.setDuration(getRemoveDuration())//設置延遲時間
                .alpha(0).setListener(new VpaListenerAdapter() {//listener是開始和結束的監聽*/
        animation.setDuration(getRemoveDuration())//設置延遲時間
                .translationX(view.getWidth()).setListener(new VpaListenerAdapter() {//listener是開始和結束的監聽
            //變到哪裏
            @Override
            public void onAnimationStart(View view) {
                dispatchRemoveStarting(holder);
            }

            @Override
            public void onAnimationEnd(View view) {
                animation.setListener(null);//當動畫結束的時候要將監聽置爲空
                //ViewCompat.setAlpha(view, 1);//因爲有複用佈局的問題,所以你將控件刪除的時候需要將他還原,要不會出現重複問題
                ViewCompat.setTranslationX(view, 0);//因爲有複用佈局的問題,所以你將控件刪除的時候需要將他還原,要不會出現重複問題
                dispatchRemoveFinished(holder);
                mRemoveAnimations.remove(holder);
                dispatchFinishedWhenDone();
            }
        }).start();
    }

我們可以發現他的動畫是先淡出然後上移,我們想要實現讓他啊滑動出屏幕後其他條目上移的話那麼簡單的將淡入淡出動畫改成移動動畫即可
    private void animateRemoveImpl(final ViewHolder holder) {
        final View view = holder.itemView;//首先得到ItemView
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);//開啓一個屬性動畫
        mRemoveAnimations.add(holder);//加入到要刪除的VIew中
        animation.setDuration(getRemoveDuration())//設置延遲時間
                .translationX(view.getWidth()).setListener(new VpaListenerAdapter() {//listener是開始和結束的監聽
            @Override
            public void onAnimationStart(View view) {
                dispatchRemoveStarting(holder);
            }

            @Override
            public void onAnimationEnd(View view) {
                animation.setListener(null);//當動畫結束的時候要將監聽置爲空
                //ViewCompat.setAlpha(view, 1);//因爲有複用佈局的問題,所以你將控件刪除的時候需要將他還原,要不會出現重複問題
                ViewCompat.setTranslationX(view,0);//因爲有複用佈局的問題,所以你將控件刪除的時候需要將他還原,要不會出現重複問題
                dispatchRemoveFinished(holder);
                mRemoveAnimations.remove(holder);
                dispatchFinishedWhenDone();
            }
        }).start();
    }
在點擊事件中設置成自己的動畫和動畫延時時間就好
 myItemAnimator = new MyItemAnimator();
                myItemAnimator.setRemoveDuration(2000);
                myRecyclerVIew.setItemAnimator(myItemAnimator);


c.添加時的動畫
先來效果圖
原動畫是先下移然後再從最淡的時候顯示出來那麼我們想讓他從側面進入那麼更改淡入淡出動畫換成移動動畫即可
源碼
 @Override
    public boolean animateAdd(final ViewHolder holder) {
        resetAnimation(holder);
        ViewCompat.setAlpha(holder.itemView, 0);
        mPendingAdditions.add(holder);
        return true;
    }

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

更改後的代碼
 @Override
    public boolean animateAdd(final ViewHolder holder) {
        resetAnimation(holder);
        //ViewCompat.setAlpha(holder.itemView, 0);
        ViewCompat.setTranslationX(holder.itemView,-holder.itemView.getWidth());
        mPendingAdditions.add(holder);
        return true;
    }

    void animateAddImpl(final ViewHolder holder) {
        final View view = holder.itemView;
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
        mAddAnimations.add(holder);
        //animation.alpha(1).setDuration(getAddDuration()).
        animation.translationX(0).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();
    }

在點擊事件中設置動畫和其時間即可
                myItemAnimator = new MyItemAnimator();
                myItemAnimator.setAddDuration(2000);
                myRecyclerVIew.setItemAnimator(myItemAnimator);


d.移動動畫,所謂的移動動畫就是當你刪除或者添加的時候其他條目的動畫,我們可以將其設置成翻轉的效果
效果圖如下

源碼
@Override
    public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
                               int toX, int toY) {
        final View view = holder.itemView;
        fromX += ViewCompat.getTranslationX(holder.itemView);
        fromY += ViewCompat.getTranslationY(holder.itemView);
        resetAnimation(holder);
        int deltaX = toX - fromX;
        int deltaY = toY - fromY;
        if (deltaX == 0 && deltaY == 0) {
            dispatchMoveFinished(holder);
            return false;
        }
        if (deltaX != 0) {
            ViewCompat.setTranslationX(view, -deltaX);
        }
        if (deltaY != 0) {
            ViewCompat.setTranslationY(view, -deltaY);
        }
        mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
        return true;
    }

    void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {
        final View view = holder.itemView;
        final int deltaX = toX - fromX;
        final int deltaY = toY - fromY;
        if (deltaX != 0) {
            ViewCompat.animate(view).translationX(0);
        }
        if (deltaY != 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.
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
        mMoveAnimations.add(holder);
        animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {
            @Override
            public void onAnimationStart(View view) {
                dispatchMoveStarting(holder);
            }
            @Override
            public void onAnimationCancel(View view) {
                if (deltaX != 0) {
                    ViewCompat.setTranslationX(view, 0);
                }
                if (deltaY != 0) {
                    ViewCompat.setTranslationY(view, 0);
                }
            }
            @Override
            public void onAnimationEnd(View view) {
                animation.setListener(null);
                dispatchMoveFinished(holder);
                mMoveAnimations.remove(holder);
                dispatchFinishedWhenDone();
            }
        }).start();
    }


在animateMoveImpl中添加我們的翻轉動畫
animation.rotationXBy(180);
因爲我們是翻轉180度那麼需要在後面進行重置
在AnimationEnd後重置
ViewCompat.setRotationX(view,0);

更改後的所有代碼
 void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {
        final View view = holder.itemView;
        final int deltaX = toX - fromX;
        final int deltaY = toY - fromY;
        if (deltaX != 0) {
            ViewCompat.animate(view).translationX(0);
        }
        if (deltaY != 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.
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
        animation.rotationXBy(180);
        mMoveAnimations.add(holder);
        animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {
            @Override
            public void onAnimationStart(View view) {
                dispatchMoveStarting(holder);
            }
            @Override
            public void onAnimationCancel(View view) {
                if (deltaX != 0) {
                    ViewCompat.setTranslationX(view, 0);
                }
                if (deltaY != 0) {
                    ViewCompat.setTranslationY(view, 0);
                }
            }
            @Override
            public void onAnimationEnd(View view) {
                animation.setListener(null);
                ViewCompat.setRotationX(view,0);
                dispatchMoveFinished(holder);
                mMoveAnimations.remove(holder);
                dispatchFinishedWhenDone();
            }
        }).start();
    }

e.改變動畫,就是移除就得得到新的,而源碼是淡出舊的顯示新的那麼進行適當更改就行了
效果圖如下:

源碼
 @Override
    public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
                                 int fromX, int fromY, int toX, int toY) {
        if (oldHolder == newHolder) {
            // Don't know how to run change animations when the same view holder is re-used.
            // run a move animation to handle position changes.
            return animateMove(oldHolder, fromX, fromY, toX, toY);
        }
        final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);
        final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);
        final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
        resetAnimation(oldHolder);
        int deltaX = (int) (toX - fromX - prevTranslationX);
        int deltaY = (int) (toY - fromY - prevTranslationY);
        // recover prev translation state after ending animation
        ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);
        ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);
        ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
        if (newHolder != null) {
            // carry over translation values
            resetAnimation(newHolder);
            ViewCompat.setTranslationX(newHolder.itemView, -deltaX);
            ViewCompat.setTranslationY(newHolder.itemView, -deltaY);
            ViewCompat.setAlpha(newHolder.itemView, 0);
        }
        mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
        return true;
    }

    void animateChangeImpl(final ChangeInfo changeInfo) {
        final ViewHolder holder = changeInfo.oldHolder;
        final View view = holder == null ? null : holder.itemView;
        final ViewHolder newHolder = changeInfo.newHolder;
        final View newView = newHolder != null ? newHolder.itemView : null;
        if (view != null) {
            final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(
                    getChangeDuration());
            mChangeAnimations.add(changeInfo.oldHolder);
            oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
            oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
            oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() {
                @Override
                public void onAnimationStart(View view) {
                    dispatchChangeStarting(changeInfo.oldHolder, true);
                }

                @Override
                public void onAnimationEnd(View view) {
                    oldViewAnim.setListener(null);
                    ViewCompat.setAlpha(view, 1);
                    ViewCompat.setTranslationX(view, 0);
                    ViewCompat.setTranslationY(view, 0);
                    dispatchChangeFinished(changeInfo.oldHolder, true);
                    mChangeAnimations.remove(changeInfo.oldHolder);
                    dispatchFinishedWhenDone();
                }
            }).start();
        }

更改後的代碼
  @Override
    public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
                                 int fromX, int fromY, int toX, int toY) {
        if (oldHolder == newHolder) {
            // Don't know how to run change animations when the same view holder is re-used.
            // run a move animation to handle position changes.
            return animateMove(oldHolder, fromX, fromY, toX, toY);
        }
        final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);
        final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);
        final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
        resetAnimation(oldHolder);
        int deltaX = (int) (toX - fromX - prevTranslationX);
        int deltaY = (int) (toY - fromY - prevTranslationY);
        // recover prev translation state after ending animation
        ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);
        ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);
        ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
        if (newHolder != null) {
            // carry over translation values
            resetAnimation(newHolder);
            ViewCompat.setTranslationX(newHolder.itemView, -deltaX);
            ViewCompat.setTranslationY(newHolder.itemView, -deltaY);
            //ViewCompat.setAlpha(newHolder.itemView, 0);
            //新的移除視線
            ViewCompat.setTranslationX(newHolder.itemView,-newHolder.itemView.getWidth());
        }
        mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
        return true;
    }

    void animateChangeImpl(final ChangeInfo changeInfo) {
        final ViewHolder holder = changeInfo.oldHolder;
        final View view = holder == null ? null : holder.itemView;
        final ViewHolder newHolder = changeInfo.newHolder;
        final View newView = newHolder != null ? newHolder.itemView : null;
        if (view != null) {
            final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(
                    getChangeDuration());
            mChangeAnimations.add(changeInfo.oldHolder);
            oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
            oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
            //oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() {
            //老的移除
            oldViewAnim.translationX(view.getWidth()).setListener(new VpaListenerAdapter() {
                @Override
                public void onAnimationStart(View view) {
                    dispatchChangeStarting(changeInfo.oldHolder, true);
                }

                @Override
                public void onAnimationEnd(View view) {
                    oldViewAnim.setListener(null);
                    ViewCompat.setAlpha(view, 1);
                    ViewCompat.setTranslationX(view, 0);
                    ViewCompat.setTranslationY(view, 0);
                    dispatchChangeFinished(changeInfo.oldHolder, true);
                    mChangeAnimations.remove(changeInfo.oldHolder);
                    dispatchFinishedWhenDone();
                }
            }).start();
        }
        if (newView != null) {
            final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView);
            mChangeAnimations.add(changeInfo.newHolder);
            newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).
                    alpha(1).setListener(new VpaListenerAdapter() {
                @Override
                public void onAnimationStart(View view) {
                    dispatchChangeStarting(changeInfo.newHolder, false);
                }
                @Override
                public void onAnimationEnd(View view) {
                    newViewAnimation.setListener(null);
                    ViewCompat.setAlpha(newView, 1);
                    ViewCompat.setTranslationX(newView, 0);
                    ViewCompat.setTranslationY(newView, 0);
                    dispatchChangeFinished(changeInfo.newHolder, false);
                    mChangeAnimations.remove(changeInfo.newHolder);
                    dispatchFinishedWhenDone();
                }
            }).start();
        }
    }


源碼:源碼鏈接
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章