Android RecyclerView的Item自訂動畫及DefaultItemAnimator源碼分析_Android

來源:互聯網
上載者:User

這是關於RecyclerView的第二篇,說的是如何自訂Item動畫,但是請注意,本文不包含動畫的具體實現方法,只是告訴大家如何去自訂動畫,如何去參考原始碼。 

我們知道,RecyclerView預設會使用DefaultItemAnimator,所以如果我們需要自訂動畫,那麼應該好好的讀讀這個類的原始碼,這樣不僅僅是學習怎麼自訂,還要學習Android的設計模式。 

先弄明白一件事,DefaultItemAnimator繼承自SimpleItemAnimator,SimpleItemAnimator繼承自RecyclerView.ItemAnimator,所以如果需要自訂動畫,最簡單的方法是繼承SimpleItemAnimator。其次,動畫的類型有四種,分別是Add、Remove、Move以及Change,這裡我們只列舉Remove,舉一反三。 

我們先看SimpleItemAnimator中的源碼,在SimpleItemAnimator中有幾個重要的方法: 

@Override public boolean animateDisappearance(@NonNull ViewHolder viewHolder,   @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {  int oldLeft = preLayoutInfo.left;  int oldTop = preLayoutInfo.top;  View disappearingItemView = viewHolder.itemView;  int newLeft = postLayoutInfo == null ? disappearingItemView.getLeft() : postLayoutInfo.left;  int newTop = postLayoutInfo == null ? disappearingItemView.getTop() : postLayoutInfo.top;  if (!viewHolder.isRemoved() && (oldLeft != newLeft || oldTop != newTop)) {   disappearingItemView.layout(newLeft, newTop,     newLeft + disappearingItemView.getWidth(),     newTop + disappearingItemView.getHeight());   if (DEBUG) {    Log.d(TAG, "DISAPPEARING: " + viewHolder + " with view " + disappearingItemView);   }   return animateMove(viewHolder, oldLeft, oldTop, newLeft, newTop);  } else {   if (DEBUG) {    Log.d(TAG, "REMOVED: " + viewHolder + " with view " + disappearingItemView);   }   return animateRemove(viewHolder);  } }

解析:這個函數是重寫RecyclerView.ItemAnimator的,介面中參數分別是ViewHolder、prelayoutInfo以及postLayoutInfo,第一個參數是指Item的ViewHolder,可以通過這個對象的itemView來擷取它的View,第二個參數是指Item刪除前的位置資訊,第三個是指新的位置資訊。再接下來會判斷ViewHolder是否已經被移除以及位置是否發生變化,然後在調用animateRemove這個抽象方法,如果我們要自訂動畫,就需要去實現它(回調思想)。

 public final void dispatchRemoveStarting(ViewHolder item) {   onRemoveStarting(item); } public void onRemoveStarting(ViewHolder item) { } 

解析:dispatchRemoveStaring個是一個final方法,也就是不能被重寫,如果我們需要處理一些在Remove開始的時候的邏輯,我們就需要在animateRemove方法中調用這個方法,這個方法會執行一個onRemoveStaring方法,這個方法就允許我們重寫,所以邏輯應該寫在onRemoveStaring中,當我們調用dispatchRemoveStaring的時候,onRemoveStaring就會被執行。 

這裡只說了兩個,但是,加上其他動作的就不只是兩個啦。。。
所以,當我們繼承了SimpleItemAnimator的時候,需要實現裡面的一些方法,一般有如下這些: 
 ① animateRemove(Add、Move和Change):這些方法會在動畫發生的時候回調,一般會在這個方法中用列表記錄每個Item的動畫以及屬性
 ② endAnimation、endAnimations:分別是在一個Item或是多個Item需要立即停止的時候回調
 ③ isRunning:如果需要順暢滑動的時候,必須要重寫這個方法,很多時候比如在網路載入的時候滑動卡頓就是這個方法邏輯不對
 ④ run'PendingAnimations:這是最重要的一個方法。因為animateDisappearence等方法返回的是animateRemove等方法返回的值,而這個方法則是根據這些值來確定是否有準備好的動畫需要播放,如果有,就會回調這個方法。在這個方法我們需要處理每一個動作(Remove、Add、Move以及Change)的動畫
 

所以,我們的一般步驟就是:
 ①建立一個SimpleItemAnimator的子類
 ②建立每個動作的動作列表
 ③重寫animateRemove等方法,當介面中有動作發生,這些函數會被回調,這裡進行記錄並返回true使得run'PendingAnimations開始執行
 ④重寫run'PendingAnimations,當③的方法返回true的時候,就認為需要執行動畫,我們需要把動畫執行的邏輯寫在這裡面
 ⑤重寫isRunning,提供動畫播放狀態,一般是返回動作列表是否為空白
 ⑥如果有需要,重寫endAnimation、endAnimations、onRemoveFinish等方法
具體的步驟有了,但是我們還不清楚該怎麼構建它,不用著急,為了方便我們,Google其實已經提供了DefaultItemAnimator,我們可以參考一些它的源碼,沒有人講的比源碼有道理,我們需要的是有足夠的耐心!
DefaultItemAnimator中定義了一些ArrayList來存放動作的資訊,如下:

 private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();private ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();@Override public boolean animateRemove(final ViewHolder holder) {  resetAnimation(holder);  mPendingRemovals.add(holder);  return true; }

解析:可以看到animatorRemove方法直接是把viewholder加入列表中,然後返回true 

@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  ......  // Next, change stuff, to run in parallel with move animations  ......  // Next, add stuff  ...... }

解析:根據上面可以知道,runPendingAnimations會執行,可看到,在這個方法中遍曆了動作列表,並讓每個Item都執行了animatorRemoveImpl方法,其他動作的方法暫時先省略,有興趣的可以自行閱讀。 

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

解析:可以看到animatorRemoveImpl方法中實現了整個動畫的具體邏輯,具體怎麼做不在本文範圍中,在我們執行了動畫之後,也就是在動畫的Listener中的onAnimatorEnd中調用了dispatchRemoveFinish,還記得這個方法嗎,它會執行onRemoveFinish方法,onRemoveFinish方法是可以供給我們重寫的。然後把item移除動作列表。 

@Override public boolean isRunning() {  return (!mPendingAdditions.isEmpty() ||    !mPendingChanges.isEmpty() ||    !mPendingMoves.isEmpty() ||    !mPendingRemovals.isEmpty() ||    !mMoveAnimations.isEmpty() ||    !mRemoveAnimations.isEmpty() ||    !mAddAnimations.isEmpty() ||    !mChangeAnimations.isEmpty() ||    !mMovesList.isEmpty() ||    !mAdditionsList.isEmpty() ||    !mChangesList.isEmpty()); }

解析:isRunning方法其實就是根據動作列表是否為空白來返回結果
還有其他一些函數可以自己閱讀原始碼。

以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援雲棲社區。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.