国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android RecyclerView的Item自定義動(dòng)畫及DefaultItemAnimator源碼分析

2019-12-12 06:03:07
字體:
供稿:網(wǎng)友

這是關(guān)于RecyclerView的第二篇,說的是如何自定義Item動(dòng)畫,但是請(qǐng)注意,本文不包含動(dòng)畫的具體實(shí)現(xiàn)方法,只是告訴大家如何去自定義動(dòng)畫,如何去參考源代碼。 

我們知道,RecyclerView默認(rèn)會(huì)使用DefaultItemAnimator,所以如果我們需要自定義動(dòng)畫,那么應(yīng)該好好的讀讀這個(gè)類的源代碼,這樣不僅僅是學(xué)習(xí)怎么自定義,還要學(xué)習(xí)Android的設(shè)計(jì)模式。 

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

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

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

解析:這個(gè)函數(shù)是重寫RecyclerView.ItemAnimator的,接口中參數(shù)分別是ViewHolder、prelayoutInfo以及postLayoutInfo,第一個(gè)參數(shù)是指Item的ViewHolder,可以通過這個(gè)對(duì)象的itemView來獲取它的View,第二個(gè)參數(shù)是指Item刪除前的位置信息,第三個(gè)是指新的位置信息。再接下來會(huì)判斷ViewHolder是否已經(jīng)被移除以及位置是否發(fā)生變化,然后在調(diào)用animateRemove這個(gè)抽象方法,如果我們要自定義動(dòng)畫,就需要去實(shí)現(xiàn)它(回調(diào)思想)。

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

解析:dispatchRemoveStaring個(gè)是一個(gè)final方法,也就是不能被重寫,如果我們需要處理一些在Remove開始的時(shí)候的邏輯,我們就需要在animateRemove方法中調(diào)用這個(gè)方法,這個(gè)方法會(huì)執(zhí)行一個(gè)onRemoveStaring方法,這個(gè)方法就允許我們重寫,所以邏輯應(yīng)該寫在onRemoveStaring中,當(dāng)我們調(diào)用dispatchRemoveStaring的時(shí)候,onRemoveStaring就會(huì)被執(zhí)行。 

這里只說了兩個(gè),但是,加上其他動(dòng)作的就不只是兩個(gè)啦。。。
所以,當(dāng)我們繼承了SimpleItemAnimator的時(shí)候,需要實(shí)現(xiàn)里面的一些方法,一般有如下這些: 
 ① animateRemove(Add、Move和Change):這些方法會(huì)在動(dòng)畫發(fā)生的時(shí)候回調(diào),一般會(huì)在這個(gè)方法中用列表記錄每個(gè)Item的動(dòng)畫以及屬性
 ② endAnimation、endAnimations:分別是在一個(gè)Item或是多個(gè)Item需要立即停止的時(shí)候回調(diào)
 ③ isRunning:如果需要順暢滑動(dòng)的時(shí)候,必須要重寫這個(gè)方法,很多時(shí)候比如在網(wǎng)絡(luò)加載的時(shí)候滑動(dòng)卡頓就是這個(gè)方法邏輯不對(duì)
 ④ run'PendingAnimations:這是最重要的一個(gè)方法。因?yàn)閍nimateDisappearence等方法返回的是animateRemove等方法返回的值,而這個(gè)方法則是根據(jù)這些值來確定是否有準(zhǔn)備好的動(dòng)畫需要播放,如果有,就會(huì)回調(diào)這個(gè)方法。在這個(gè)方法我們需要處理每一個(gè)動(dòng)作(Remove、Add、Move以及Change)的動(dòng)畫
 

所以,我們的一般步驟就是:
 ①創(chuàng)建一個(gè)SimpleItemAnimator的子類
 ②創(chuàng)建每個(gè)動(dòng)作的動(dòng)作列表
 ③重寫animateRemove等方法,當(dāng)界面中有動(dòng)作發(fā)生,這些函數(shù)會(huì)被回調(diào),這里進(jìn)行記錄并返回true使得run'PendingAnimations開始執(zhí)行
 ④重寫run'PendingAnimations,當(dāng)③的方法返回true的時(shí)候,就認(rèn)為需要執(zhí)行動(dòng)畫,我們需要把動(dòng)畫執(zhí)行的邏輯寫在這里面
 ⑤重寫isRunning,提供動(dòng)畫播放狀態(tài),一般是返回動(dòng)作列表是否為空
 ⑥如果有需要,重寫endAnimation、endAnimations、onRemoveFinish等方法
具體的步驟有了,但是我們還不清楚該怎么構(gòu)建它,不用著急,為了方便我們,谷歌其實(shí)已經(jīng)提供了DefaultItemAnimator,我們可以參考一些它的源碼,沒有人講的比源碼有道理,我們需要的是有足夠的耐心!
DefaultItemAnimator中定義了一些ArrayList來存放動(dòng)作的信息,如下:

 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  ...... }

解析:根據(jù)上面可以知道,runPendingAnimations會(huì)執(zhí)行,可看到,在這個(gè)方法中遍歷了動(dòng)作列表,并讓每個(gè)Item都執(zhí)行了animatorRemoveImpl方法,其他動(dòng)作的方法暫時(shí)先省略,有興趣的可以自行閱讀。 

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方法中實(shí)現(xiàn)了整個(gè)動(dòng)畫的具體邏輯,具體怎么做不在本文范圍中,在我們執(zhí)行了動(dòng)畫之后,也就是在動(dòng)畫的Listener中的onAnimatorEnd中調(diào)用了dispatchRemoveFinish,還記得這個(gè)方法嗎,它會(huì)執(zhí)行onRemoveFinish方法,onRemoveFinish方法是可以供給我們重寫的。然后把item移除動(dòng)作列表。 

@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方法其實(shí)就是根據(jù)動(dòng)作列表是否為空來返回結(jié)果
還有其他一些函數(shù)可以自己閱讀源代碼。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 本溪市| 石家庄市| 三台县| 澄江县| 江孜县| 沙坪坝区| 资溪县| 弋阳县| 原阳县| 鹤壁市| 横峰县| 浮梁县| 宜阳县| 资兴市| 卢氏县| 永修县| 淮安市| 敦化市| 沂源县| 交城县| 成都市| 保亭| 吉水县| 镇宁| 河间市| 修水县| 夏津县| 邛崃市| 安宁市| 仙桃市| 大竹县| 威信县| 永修县| 子洲县| 万山特区| 靖安县| 台前县| 济源市| 潍坊市| 罗江县| 信丰县|