ItemAnimator
做爲RecyclerView
的主要組成部分之一,其重要性天然不可言喻。由於ItemAnimator
的存在,因此出現了不少精彩紛呈的動畫,這使得RecyclerView
更加惹人喜好。所以,學習ItemAnimator
的源碼是勢在必行的,由於咱們瞭解原理以後,就能夠自定義動畫了,再也不受人束縛。git
本文參考資料:github
本文打算才以下幾個部分來分析ItemAnimator
的源碼:數組
ItemAnimator
類相關方法的分析和總結。SimpleItemAnimator
相關代碼的分析。DefaultItemAnimator
相關代碼的分析。- 自定義一個
ItemAnimator
。
首先咱們來對ItemAnimator
的整個結構作一個簡單的概述,方便你們理解。bash
一般來講,自定義一個ItemAnimator
的過程是:ItemAnimator
->SimpleItemAnimator
-> 自定義ItemAnimator
。包括官方提供的DefaultItemAnimator
也是這樣來定義的,那這樣來定義有好處的呢?這樣定義,結構層次比較清晰,咱們自定義ItemAnimator
比較方便,只須要關注動畫怎麼實現就行。咱們來看看這三層分別幹了什麼:架構
結構層次 | 做用 |
---|---|
ItemAnimator | 定義ItemAnimator 的模板,自定義這裏包括4個抽象方法,也是很是重要的抽象方法,分別是:animateDisappearance 、animateAppearance 、animatePersistence 和animateChange 。 |
SimpleItemAnimator | 實現了四個抽象方法,根據調用4個抽象方法的時機不一樣,因此會作不一樣的動畫,因此又對外提供了4個抽象方法,分別是:animateRemove 、animateAdd ,animateMove 和animateChange ,分別對應刪除、添加、移動和改變的動畫。 |
自定義ItemAnimator |
主要實現4種操做的動畫,也包括結束動畫相關實現 |
而咱們在自定義ItemAnimator
時,只須要考慮第三層就OK,上面兩層的邏輯谷歌爸爸已經幫咱們實現了。自定義過ItemAnimator
的同窗應該都知道,儘管只關注第三層,可是實現仍是那麼麻煩,介於這個問題,我會提出一個很是簡單的自定義itemAnimator
的方案。app
咱們從上往下,看看每一層都爲咱們作了哪些事情,首先咱們來了解一下ItemAnimator
。ItemAnimator
總的來講比較簡單,咱們來看看ItemAnimator
幾個方法:ide
方法名 | 做用 |
---|---|
animateDisappearance | 抽象方法,供第二層實現。此方法的調用時機是ItemView 從當前的可見區域消失,其中包括:1.ItemView 執行了remove操做;2. ItemView 執行move操做移動到不可見區域。在此方法裏面,根據不一樣的狀況,執行move動畫或者執行remove動畫 |
animateAppearance | 抽象方法,供第二層實現。此方法的調用時機是ItemView 出如今可見區域,其中包括:1. ItemView 執行了add操做;2. ItemView 執行move操做從不可見區域移動到可見區域。在此方法裏面,根據不一樣的狀況,執行add動畫或者執行move動畫。 |
animatePersistence | 抽象方法,供第二層實現。此方法的調用時機是ItemView 未進行任何操做。在此方法裏面,根據不一樣的狀況,會執行remove動畫(好比說當前ItemView 上面有一個ItemView 執行了reomve操做)或者無任何動畫。 |
animateChange | 抽象方法,供第二層實現。此方法的調用時機是ItemView 進行了change操做。在方法裏面,會執行change動畫。 |
在ItemAnimator
中,上面4個方法很是的重要,RecyclerView
就是經過這四個方法來給每一個ItemView
添加不一樣的動畫。在這一層,咱們須要掌握的就是,記住這4個方法的調用時機,這樣咱們在看SimpleItemAnimator
代碼時,纔不會不知所措。源碼分析
就像在上面概述所說的同樣,SimpleItemAnimator
處於第二層,負責實現ItemAnimator
的4個抽象方法,而後又提供了四種操做須要分別調用的抽象方法,這樣作就更加細化了動畫執行的狀況,簡化了自定義ItemAnimator
的過程。post
在正式看SimpleItemAnimator
的源碼以前,咱們先來看看SimpleItemAnimator
幾個方法的介紹。學習
方法名 | 參數 | 說明 |
---|---|---|
animateRemove | ViewHolder | 噹噹前的ItemView 執行了remove操做須要執行remove動畫時,會回調此方法。 |
animateAdd | ViewHolder | 噹噹前的ItemView 執行了add操做須要執行add動畫時,會回調此方法。 |
animateMove | ViewHolder | 噹噹前的ItemView 執行了move操做,或者它以前有ItemView 執行了remove操做或者add操做,會回調此方法。 |
animateChange | ViewHolder oldHolder,ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop | 噹噹前的ItemView 執行了change操做,會調用此方法。 |
咱們看到到了SimpleItemAnimator
這一層,每一個ItemView
應該作什麼動畫,如今已經一清二楚了,再也不像第一層裏面那樣,一個方法裏面可能涉及到多種狀況,每種狀況可能執行不一樣的動畫。 如今咱們分別來看看SimpleItemAnimator
對ItemAnimator
的4個抽象方法的實現,看看他是怎麼來判斷一個ItemView
執行何種動畫的。
首先,咱們來看一下animateDisappearance
方法:
@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);
}
}
複製代碼
animateDisappearance
方法表達的意思很是簡單,首先判斷當前ItemView
是否須要執行的是move動畫,若是是,那麼就調用animateMove
方法;若是不是的話,那麼就調用animateRemove
方法用來執行remove動畫。
在這個方法裏面,remove操做咱們理解,可是move操做是什麼意思呢?首先咱們得搞清楚animateDisappearance
方法的調用時機,animateDisappearance
方法表示在ItemView
從可見狀態變爲不可見狀態,這裏包括:remove操做和ItemView
從可見區域移動到不可見區域。因此在animateDisappearance
方法裏面,執行move動畫並不意外。
而後,咱們來看一下animateAppearance
方法:
@Override
public boolean animateAppearance(@NonNull ViewHolder viewHolder,
@Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
if (preLayoutInfo != null && (preLayoutInfo.left != postLayoutInfo.left
|| preLayoutInfo.top != postLayoutInfo.top)) {
// slide items in if before/after locations differ
if (DEBUG) {
Log.d(TAG, "APPEARING: " + viewHolder + " with view " + viewHolder);
}
return animateMove(viewHolder, preLayoutInfo.left, preLayoutInfo.top,
postLayoutInfo.left, postLayoutInfo.top);
} else {
if (DEBUG) {
Log.d(TAG, "ADDED: " + viewHolder + " with view " + viewHolder);
}
return animateAdd(viewHolder);
}
}
複製代碼
animateAppearance
方法表示在ItemView
從不可見狀態變爲可見狀態,因此這裏包括add操做和move操做。move操做表示的意思跟animateDisappearance
方法的差很少。
而後,咱們再來看看animatePersistence
方法:
public boolean animatePersistence(@NonNull ViewHolder viewHolder,
@NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) {
if (DEBUG) {
Log.d(TAG, "PERSISTENT: " + viewHolder
+ " with view " + viewHolder.itemView);
}
return animateMove(viewHolder,
preInfo.left, preInfo.top, postInfo.left, postInfo.top);
}
dispatchMoveFinished(viewHolder);
return false;
}
複製代碼
animatePersistence
方法比其餘方法都簡單,這裏只進行了move動畫。固然若是不執行任何動畫,這裏會返回false,而且會調用dispatchMoveFinished
方法,這是基本要求,當每一個動畫執行完畢以後,都是調用相關方法來通知動畫執行結束了。
最後,咱們再來看看animateChange
方法:
@Override
public boolean animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
@NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
if (DEBUG) {
Log.d(TAG, "CHANGED: " + oldHolder + " with view " + oldHolder.itemView);
}
final int fromLeft = preInfo.left;
final int fromTop = preInfo.top;
final int toLeft, toTop;
if (newHolder.shouldIgnore()) {
toLeft = preInfo.left;
toTop = preInfo.top;
} else {
toLeft = postInfo.left;
toTop = postInfo.top;
}
return animateChange(oldHolder, newHolder, fromLeft, fromTop, toLeft, toTop);
}
複製代碼
animateChange
方法更加的簡單,最後始終調用了animateChange
抽象方法。
咱們在看DefaultItemAnimator
的源碼以前,先來看看它有哪些成員變量:
private static TimeInterpolator sDefaultInterpolator;
private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();
private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();
private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();
ArrayList<ArrayList<ViewHolder>> mAdditionsList = new ArrayList<>();
ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();
ArrayList<ViewHolder> mAddAnimations = new ArrayList<>();
ArrayList<ViewHolder> mMoveAnimations = new ArrayList<>();
ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();
ArrayList<ViewHolder> mChangeAnimations = new ArrayList<>();
複製代碼
在DefaultItemAnimator
的內部,成員變量主要分爲4個部分。這其中,sDefaultInterpolator
就是給ItemView
設置的一個動畫插值器對象,在DefaultItemAnimator
內部,這個插值器是AccelerateDecelerateInterpolator
插值器;而後第二部分mPendingxxx
數組,這部分就是用來存儲每一個ItemView
須要作動畫的相關信息,例如,move動畫就須要移動的起始位置和終點位置,這部分的數組纔是作動畫的真正作動畫須要的;第三部分和第四部分是mXXXList
和mXXXAnimations
,一般來講,都是用於結束動畫的,本文後面會簡單的分析他們。
簡單的瞭解這四部分的成員變量以後,如今咱們重點看一下四個抽象方法的實現。
@Override
public boolean animateRemove(final ViewHolder holder) {
resetAnimation(holder);
mPendingRemovals.add(holder);
return true;
}
複製代碼
animateRemove
方法的實現很是簡單,就是往mPendingRemovals
數組裏面添加一個元素。
而後,咱們再來看一下animateAdd
方法:
@Override
public boolean animateAdd(final ViewHolder holder) {
resetAnimation(holder);
holder.itemView.setAlpha(0);
mPendingAdditions.add(holder);
return true;
}
複製代碼
animateAdd
方法的實現也是很是簡單。這裏咱們看到有一個操做就是holder.itemView.setAlpha(0)
,咱們都知道,在DefaultItemAnimator
中,add動畫是一個漸現的過程,這裏先將ItemView
的alpha值設置0就容易理解了。
而後,咱們在來看一下animateMove
方法的實現:
@Override
public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
int toX, int toY) {
final View view = holder.itemView;
fromX += (int) holder.itemView.getTranslationX();
fromY += (int) holder.itemView.getTranslationY();
resetAnimation(holder);
int deltaX = toX - fromX;
int deltaY = toY - fromY;
if (deltaX == 0 && deltaY == 0) {
dispatchMoveFinished(holder);
return false;
}
if (deltaX != 0) {
view.setTranslationX(-deltaX);
}
if (deltaY != 0) {
view.setTranslationY(-deltaY);
}
mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
return true;
}
複製代碼
相對來講,animateMove
的實現比其餘操做都要複雜一些,可是再怎麼複雜,其實就作了兩件事:
- 根據狀況,來設置
View
的translationX
或者translationY
。- 向
mPendingMoves
數組裏面添加一個MoveInfo
,用於move動畫使用。
最後,咱們在來看看animateChange
方法的實現:
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 = oldHolder.itemView.getTranslationX(); final float prevTranslationY = oldHolder.itemView.getTranslationY(); final float prevAlpha = oldHolder.itemView.getAlpha(); resetAnimation(oldHolder); int deltaX = (int) (toX - fromX - prevTranslationX); int deltaY = (int) (toY - fromY - prevTranslationY); // recover prev translation state after ending animation oldHolder.itemView.setTranslationX(prevTranslationX); oldHolder.itemView.setTranslationY(prevTranslationY); oldHolder.itemView.setAlpha(prevAlpha); if (newHolder != null) { // carry over translation values resetAnimation(newHolder); newHolder.itemView.setTranslationX(-deltaX); newHolder.itemView.setTranslationY(-deltaY); newHolder.itemView.setAlpha(0); } mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY)); return true; } 複製代碼
而change操做比較特殊,哪裏特殊呢?主要有兩點:
- 它涉及到從舊狀態變成新狀態,因此須要考慮兩個
ItemView
的存在。- 在作change操做時,可能會作move操做。因此在這裏還設置了
ItemView
的translation
。
這裏只是簡單的介紹了ItemView
可能同時進行change操做和move操做,待會在將動畫實現時,咱們來看一下DefaultItemAnimator
是怎麼實現兩個動畫同時進行的。
咱們往數組裏面添加了不少須要作動畫的元素,可是何時開始執行這些動畫呢?其實咱們在RecyclerView 源碼分析(四) - RecyclerView的動畫機制這篇文章裏面已經分析了RecyclerView
是怎麼開始動畫的。其實就是回調了ItemAnimator
的一個方法--runPendingAnimations
方法,以前咱們添加的全部動畫都在方法裏面執行。咱們在看runPendingAnimations
方法以前,咱們有幾個問題,咱們帶着問題去看思路會更加的清晰,一共兩個問題:
DefaultItemAnimator
是怎麼實現每種操做的動畫呢?- 咱們知道,在
RecyclerView
中,某些操做的動畫是時序,好比說,必須在remove動畫執行完畢以後,纔會執行move動畫,這個又是怎麼實現呢?
經過閱讀runPendingAnimations
方法的源碼,咱們能夠將它的源碼分爲4個部分,分別以下:
- remove動畫的執行
- move動畫的執行
- change動畫的執行
- add動畫的執行
咱們分別來看看這四部分的代碼。
// First, remove stuff
for (ViewHolder holder : mPendingRemovals) {
animateRemoveImpl(holder);
}
mPendingRemovals.clear();
複製代碼
經過上面的代碼,咱們知道,remove動畫的實現關鍵在於animateRemoveImpl
方法,咱們來看看animateRemoveImpl
方法的實現:
private void animateRemoveImpl(final ViewHolder holder) {
final View view = holder.itemView;
final ViewPropertyAnimator animation = view.animate();
mRemoveAnimations.add(holder);
animation.setDuration(getRemoveDuration()).alpha(0).setListener(
new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchRemoveStarting(holder);
}
@Override
public void onAnimationEnd(Animator animator) {
animation.setListener(null);
view.setAlpha(1);
dispatchRemoveFinished(holder);
mRemoveAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
}
複製代碼
在這裏,咱們知道,ItemView
的remove動畫是經過ViewPropertyAnimator
來實現的。在這裏咱們須要注意幾點:
- 在動畫開始以前,往
mRemoveAnimations
數組裏面添加了一個元素,主要是用於結束動畫的操做。當咱們在結束動畫時,發現mRemoveAnimations
數組裏面還有元素,表示還有remove動畫沒有執行完畢,因此結束它。- 在
onAnimationEnd
方法裏面,咱們分別調用了dispatchRemoveFinished
方法和dispatchFinishedWhenDone
。這兩步操做是必須的,並且注意他們的時序。
至於ViewPropertyAnimator
實現動畫爲何須要這樣來講,這不是本文的重點,這裏就不介紹了,你們有興趣能夠去簡單學習一下。
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();
}
}
複製代碼
這部分的代碼相對來講要複雜一點點,咱們來簡單的分析一下。上面的代碼,咱們須要注意如以下幾點:
- 將move動畫相關的動畫元素原封不動的添加到
mMovesList
數組裏面。這樣作的目的是,由於move動畫的開始有延遲,得等待remove
動畫執行完畢以後才執行。因此,存在咱們在結束動畫時,move動畫尚未開始執行的狀況,因此得先添加進去,以便結束move動畫。- 從這裏咱們就能夠知道,
DefaultItemAnimator
是怎麼解決動畫的時序問題。這裏經過ViewCompat
的postOnAnimationDelayed
來作一個延遲執行,保證rmove動畫執行完畢才執行move動畫。
而後,咱們在來看看animateMoveImpl
方法是怎麼實現move動畫的:
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) {
view.animate().translationX(0);
}
if (deltaY != 0) {
view.animate().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 ViewPropertyAnimator animation = view.animate();
mMoveAnimations.add(holder);
animation.setDuration(getMoveDuration()).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchMoveStarting(holder);
}
@Override
public void onAnimationCancel(Animator animator) {
if (deltaX != 0) {
view.setTranslationX(0);
}
if (deltaY != 0) {
view.setTranslationY(0);
}
}
@Override
public void onAnimationEnd(Animator animator) {
animation.setListener(null);
dispatchMoveFinished(holder);
mMoveAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
}
複製代碼
animateMoveImpl
方法實現move動畫其實跟remove的實現差很少,都是經過ViewPropertyAnimator
來實現的,因此這裏就再也不分析了。
而後咱們再來看看change動畫的實現:
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();
}
}
複製代碼
上面的代碼跟move
動畫那部分的實現都差很少,這裏就再也不分析了,咱們這裏重點是看animateChangeImpl
方法:
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 ViewPropertyAnimator oldViewAnim = view.animate().setDuration(
getChangeDuration());
mChangeAnimations.add(changeInfo.oldHolder);
oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
oldViewAnim.alpha(0).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchChangeStarting(changeInfo.oldHolder, true);
}
@Override
public void onAnimationEnd(Animator animator) {
oldViewAnim.setListener(null);
view.setAlpha(1);
view.setTranslationX(0);
view.setTranslationY(0);
dispatchChangeFinished(changeInfo.oldHolder, true);
mChangeAnimations.remove(changeInfo.oldHolder);
dispatchFinishedWhenDone();
}
}).start();
}
if (newView != null) {
final ViewPropertyAnimator newViewAnimation = newView.animate();
mChangeAnimations.add(changeInfo.newHolder);
newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration())
.alpha(1).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchChangeStarting(changeInfo.newHolder, false);
}
@Override
public void onAnimationEnd(Animator animator) {
newViewAnimation.setListener(null);
newView.setAlpha(1);
newView.setTranslationX(0);
newView.setTranslationY(0);
dispatchChangeFinished(changeInfo.newHolder, false);
mChangeAnimations.remove(changeInfo.newHolder);
dispatchFinishedWhenDone();
}
}).start();
}
}
複製代碼
change跟其餘比較起來,實現起來就複雜的多。咱們來簡單的分析一下:
- 在
animateChangeImpl
方法裏面,主要是有兩個動畫在執行,一個是舊的ItemView
漸隱動畫,一個是新的ItemView
漸現動畫。- 在change動畫裏面,同時包含了兩部分屬性的動畫,一個是位置的漸變,一個是透明度的漸變。從這裏,咱們就能夠看到,一個在作change動畫的
ItemView
也有可能作move
動畫。因此,在runPendingAnimations
方法裏面,4個步驟獨立執行,一個ViewHolder
不可能同時走了其中兩個或者兩個以上的步驟。
這裏add動畫沒有特殊的操做,就是簡單的漸現,咱們就不作多餘的介紹了。
你們想要自定以一個ItemAnimator
,第一個想法就是先去網上搜索一下自定以ItemAnimator
的基本步驟。我相信你們搜索到的都是一開始就叫咱們實現不少不少的方法,又不知道這些的做用,有些可能還簡單介紹了一下每一個方法的做用,可是根本不知道這個方法的具體實現。爲何須要這麼來實現動畫呢?這些都是網上大多數文章沒有介紹清楚的。
這裏,我提出一種自定義ItemAnimator
的解決方案,有多是目前最簡單的方案。
一般上面的源碼的學習,咱們瞭解了DefaultItemAnimator
的實現原理。因此,咱們自定義一個ItemAnimator
,能夠參考DefaultItemAnimator
的實現。從而,咱們自定義一個ItemAnimator
,須要解決以下幾個問題:
- 須要考慮動畫執行的時序。
- 須要考慮結束動畫。
- 實現每種動畫的實現邏輯。
從上面的問題中,咱們發現問題1和問題2,DefaultItemAnimator
已經幫咱們實現了,咱們只須要解決問題3就好了。因此,個人解決方案就是:**將DefaultItemAnimator
的拷貝出來,咱們只須要改寫animateRemoveImpl
、animateAddImpl
、animateMoveImpl
和animateChangeImpl
這四個方法就好了。**一般來講,change動畫和move動畫,咱們都不會改寫,因此重點在於add動畫和remove動畫。
如今,我來給你們看一下我自定義的一個動畫,效果以下:
關於代碼部分,我幾乎沒有動DefaultItemAnimator
的架構,只是改寫了animateRemoveImpl
方法和animateAddImpl
方法,咱們來看看:
private void animateRemoveImpl(final ViewHolder holder) {
final View view = holder.itemView;
final ViewPropertyAnimator animation = view.animate();
mRemoveAnimations.add(holder);
animation.setDuration(getRemoveDuration()).translationX(view.getRootView().getWidth()).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchRemoveStarting(holder);
}
@Override
public void onAnimationEnd(Animator animator) {
animation.setListener(null);
view.setTranslationX(0);
dispatchRemoveFinished(holder);
mRemoveAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
}
void animateAddImpl(final ViewHolder holder) {
final View view = holder.itemView;
final ViewPropertyAnimator animation = view.animate();
mAddAnimations.add(holder);
animation.translationX(0).setDuration(getAddDuration())
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchAddStarting(holder);
}
@Override
public void onAnimationCancel(Animator animator) {
view.setAlpha(0);
}
@Override
public void onAnimationEnd(Animator animator) {
animation.setListener(null);
dispatchAddFinished(holder);
mAddAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
}
複製代碼
實現的邏輯也是很是簡單,就是將DefaultItemAnimator
的alpha變化改成了translationX
的變化。如上所示,自定義一個ItemAnimator
就是這麼簡單!
若是還有不懂的同窗,能夠去個人github去下載Demo:ItemAnimatorDemo。
關於ItemAnimator
的源碼分析到此就已經結束了,我在這裏作一個簡單的總結。
- 理解
ItemAnimator
的原理以前,咱們最好先了解它的三層模型,分別是ItemAnimator
、SimpleItemAnimator
和DefaultItemAnimator
,咱們須要搞清楚每一層到底爲咱們作了哪些事情。- 自定義
ItemAnimator
咱們只須要拷貝DefaultItemAnimator
的代碼,而後根據本身的要求分別改寫animateRemoveImpl
、animateAddImpl
、animateMoveImpl
和animateChangeImpl
這四個方法就好了。
到此爲止,RecyclerView
源碼分析系列的文章就結束了。經過這系列的文章中,咱們全方位立體的瞭解了RecyclerView
的各個方面,使得咱們對RecyclerView
的理解更上一層樓了。固然這個過程當中確定有地方缺乏了,就好比說:ItemTouchHelper
或者ItemDecoration
,這部分的內容相對來講簡單一點,後續我會推出一個RecyclerView
的拓展系列,來補充這部分的內容。
人非聖賢,孰能無過!各位大佬在閱讀過程當中若是發現錯誤或者有疑惑,均可以提出來,歡迎大佬們斧正!