RecyclerView已經(jīng)普及使用,其各式各樣的布局格式娄徊,以及眾多的優(yōu)越特性趟据,使得RecyclerView具有很大的靈活性。其中之一便是ItemAnimator埠对,通過自定義ItemAnimator可以實現(xiàn)各種各樣的Item增加络断,刪除,改變项玛,移動等動畫效果貌笨。這也是本篇文章的主要內(nèi)容。
一襟沮、recyclerview-animators
本篇文章將圍繞recyclerview-animators展開锥惋,先介紹其簡單使用,然后介紹其實現(xiàn)原理臣嚣。
首先净刮,介紹recyclerview-animators的效果圖。GitHub地址:recyclerview-animators
這個庫主要實現(xiàn)兩部分內(nèi)容硅则。
一:自定義ItemAnimator淹父,實現(xiàn)Item增加和刪除的動畫效果。
二:對Adapter進行封裝怎虫,在onBindViewHolder中暑认,綁定過程中設(shè)置顯示動畫困介。
效果圖
自定義ItemAnimator效果
Adapter動畫效果
二、recyclerview-animators的使用
ItemAnimator的使用
1.引入依賴
compile 'jp.wasabeef:recyclerview-animators:2.2.6'
2.使用
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);
recyclerView.setItemAnimator(new SlideInLeftAnimator());
3.高級功能
動畫時長
recyclerView.getItemAnimator().setAddDuration(1000);
recyclerView.getItemAnimator().setRemoveDuration(1000);
recyclerView.getItemAnimator().setMoveDuration(1000);
recyclerView.getItemAnimator().setChangeDuration(1000);
插值器
SlideInLeftAnimator animator = new SlideInLeftAnimator();
animator.setInterpolator(new OvershootInterpolator());
recyclerView.setItemAnimator(animator);
另外自定義動畫實現(xiàn)
static class MyViewHolder extends RecyclerView.ViewHolder implements AnimateViewHolder {
public MyViewHolder(View itemView) {
super(itemView);
}
@Override
public void preAnimateRemoveImpl(RecyclerView.ViewHolder holder) {
}
@Override
public void animateRemoveImpl(RecyclerView.ViewHolder holder, ViewPropertyAnimatorListener listener) {
ViewCompat.animate(itemView)
.translationY(-itemView.getHeight() * 0.3f)
.alpha(0)
.setDuration(300)
.setListener(listener)
.start();
}
@Override
public void preAnimateAddImpl(RecyclerView.ViewHolder holder) {
ViewCompat.setTranslationY(itemView, -itemView.getHeight() * 0.3f);
ViewCompat.setAlpha(itemView, 0);
}
@Override
public void animateAddImpl(RecyclerView.ViewHolder holder, ViewPropertyAnimatorListener listener) {
ViewCompat.animate(itemView)
.translationY(0)
.alpha(1)
.setDuration(300)
.setListener(listener)
.start();
}
}
4.注意
使用以下方式蘸际,才會觸發(fā)動畫效果座哩,具體下面分析:
notifyItemInserted(int)
notifyItemRemoved(int)
notifyItemRangeInserted(int, int)
notifyItemRangeRemoved(int, int)
例如:
public void remove(int position) {
mDataSet.remove(position);
notifyItemRemoved(position);
}
public void add(String text, int position) {
mDataSet.add(position, text);
notifyItemInserted(position);
}
Adapter的使用
1.使用
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);
MyAdapter adapter = new MyAdapter();
recyclerView.setAdapter(new AlphaInAnimationAdapter(adapter));
2.高級功能
動畫時長
MyAdapter adapter = new MyAdapter();
AlphaInAnimationAdapter alphaAdapter = new AlphaInAnimationAdapter(adapter);
alphaAdapter.setDuration(1000);
recyclerView.setAdapter(alphaAdapter);
插值器
MyAdapter adapter = new MyAdapter();
AlphaInAnimationAdapter alphaAdapter = new AlphaInAnimationAdapter(adapter);
alphaAdapter.setInterpolator(new OvershootInterpolator());
recyclerView.setAdapter(alphaAdapter);
是否僅顯示一次動畫效果
MyAdapter adapter = new MyAdapter();
AlphaInAnimationAdapter alphaAdapter = new AlphaInAnimationAdapter(adapter);
scaleAdapter.setFirstOnly(false);
recyclerView.setAdapter(alphaAdapter);
復(fù)合動畫
MyAdapter adapter = new MyAdapter();
AlphaInAnimationAdapter alphaAdapter = new AlphaInAnimationAdapter(adapter);
recyclerView.setAdapter(new ScaleInAnimationAdapter(alphaAdapter));
三、自定義ItemAnimator實現(xiàn)原理
上面我們分析了recyclerview-animators的實現(xiàn)粮彤,那么我們?nèi)绾螌崿F(xiàn)自己想要的更炫酷的動畫方式呢根穷,以及上述注意中為什么只能使用那幾種方式才能實現(xiàn)動畫效果呢?這一切都需要理解其原理才行导坟,下面我們主要分析自定義動畫的實現(xiàn)屿良。
1.類結(jié)構(gòu)
首先,所有的自定義ItemAnimator都是繼承BaseItemAnimator實現(xiàn)的惫周。
public abstract class BaseItemAnimator extends SimpleItemAnimator
而SimpleItemAnimator 又是什么尘惧? 是RecyclerView中根據(jù)ItemAnimator進行的一些簡單的封裝。
abstract public class SimpleItemAnimator extends RecyclerView.ItemAnimator
RecyclerView有著默認(rèn)的動畫效果實現(xiàn)DefaultItemAnimator递递,也繼承了SimpleItemAnimator 喷橙,后面具體比較分析。
一切的源頭都是ItemAnimator登舞,所以先來了解下它的主要方法贰逾。
2.ItemAnimator
當(dāng)RecyclerView中的item在屏幕上由可見變?yōu)椴豢梢姇r調(diào)用此方法
public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
@NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
當(dāng)RecyclerView中的item顯示到屏幕上時調(diào)用此方法
public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
@Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
當(dāng)RecyclerView中的item狀態(tài)發(fā)生改變時調(diào)用此方法(notifyItemChanged(position))
public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
@NonNull ViewHolder newHolder,
@NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
統(tǒng)籌RecyclerView中所有的動畫,統(tǒng)一啟動執(zhí)行
abstract public void runPendingAnimations();
這幾個方法是自定義ItemAnimator的關(guān)鍵逊躁,實現(xiàn)不同的動畫效果似踱。
SimpleItemAnimator 在ItemAnimator的基礎(chǔ)上,針對上述幾個方法稽煤,進行了一定的封裝核芽,使得可以更專注于動畫的實現(xiàn)。
3.SimpleItemAnimator
SimpleItemAnimator對這幾個方法是如何封裝的呢酵熙?
以添加一個Item舉例轧简,最后調(diào)用的是animateAppearance方法,SimpleItemAnimator的animateAppearance方法代碼如下:
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);
}
}
其實封裝的很簡單匾二,只是判斷了一下布局前后兩個ViewHolder的left/top坐標(biāo)是否相等 如果不相等則調(diào)用animateMove哮独,否則調(diào)用animateAdd(viewHolder)方法。
其他方法類似察藐,最后我們只需要實現(xiàn)animateRemove, animateAdd, animateMove, animateChange 這4個方法皮璧。
下面我們先看看RecyclerView中DefaultItemAnimator的實現(xiàn),再來分析BaseItemAnimator分飞。
4.DefaultItemAnimator
首先是對animateRemove, animateAdd, animateMove, animateChange 這4個方法的實現(xiàn)悴务。
還是以animateAdd為例:
public boolean animateAdd(final ViewHolder holder) {
resetAnimation(holder);
ViewCompat.setAlpha(holder.itemView, 0);
mPendingAdditions.add(holder);
return true;
}
首先調(diào)用resetAnimation(holder)停止此holder中itemView的動畫效果,然后將holder.itemView的透明度設(shè)置為0不可見狀態(tài)(也可以設(shè)置其他屬性,相當(dāng)于動畫開始的初始效果)讯檐。
將holder添加到mPendingAdditions集合中羡疗。
注意:此集合一會兒會在runPendingAnimations方法中進行遍歷執(zhí)行動畫。
然后是對runPendingAnimations的實現(xiàn)别洪。
下面上源碼叨恨,很長,不過別害怕挖垛,很簡單痒钝。
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
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();
}
}
// Next, change stuff, to run in parallel with move animations
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();
}
}
// Next, add stuff
if (additionsPending) {
final ArrayList<ViewHolder> additions = new ArrayList<>();
additions.addAll(mPendingAdditions);
mAdditionsList.add(additions);
mPendingAdditions.clear();
Runnable adder = new Runnable() {
@Override
public void run() {
for (ViewHolder holder : additions) {
animateAddImpl(holder);
}
additions.clear();
mAdditionsList.remove(additions);
}
};
if (removalsPending || movesPending || changesPending) {
long removeDuration = removalsPending ? getRemoveDuration() : 0;
long moveDuration = movesPending ? getMoveDuration() : 0;
long changeDuration = changesPending ? getChangeDuration() : 0;
long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
View view = additions.get(0).itemView;
ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
} else {
adder.run();
}
}
}
remove最先執(zhí)行,for循環(huán)遍歷mPendingRemovals集合中所有的ViewHolder晕换。remove完成后午乓,再同時開始move和change動畫,最后執(zhí)行add動畫闸准。
其中,需要注意幾個重要的方法:
animateRemoveImpl(holder);
animateMoveImpl(moveInfo.holder,moveInfo.fromX, moveInfo.fromY,moveInfo.toX, moveInfo.toY);
animateChangeImpl(change);
animateAddImpl(holder);
這幾個方法執(zhí)行具體的動畫效果梢灭,具體來看源碼夷家。
下面來分析下BaseItemAnimator的實現(xiàn)。
5.BaseItemAnimator
了解了DefaultItemAnimator敏释,那么對BaseItemAnimator的實現(xiàn)就清楚了库快。
首先BaseItemAnimator使用了DefaultItemAnimator中的runPendingAnimations()方法的實現(xiàn)。
其次BaseItemAnimator使用了DefaultItemAnimator中的animateMove, animateChange钥顽,以及animateRemoveImpl义屏,animateChangeImpl等方法的實現(xiàn)。
所以BaseItemAnimator只留下了animateRemove, animateAdd方法蜂大,方便我們自定義實現(xiàn)闽铐。
以animateAdd方法為例:
@Override public boolean animateAdd(final ViewHolder holder) {
endAnimation(holder);
preAnimateAdd(holder);
mPendingAdditions.add(holder);
return true;
}
第一步:停止此holder中itemView的動畫效果,與DefaultItemAnimator一致奶浦。
第二步:初始化itemView的屬性
第三步:將holder添加到mPendingAdditions集合中兄墅。
與DefaultItemAnimator的不同主要是第二步,詳細看看澳叉。
private void preAnimateAdd(final ViewHolder holder) {
ViewHelper.clear(holder.itemView);
if (holder instanceof AnimateViewHolder) {
((AnimateViewHolder) holder).preAnimateAddImpl(holder);
} else {
preAnimateAddImpl(holder);
}
}
如果holder屬于AnimateViewHolder類或子類隙咸,那么就調(diào)用其preAnimateAddImpl()方法,否則調(diào)用內(nèi)部的preAnimateAddImpl()方法成洗。
protected void preAnimateAddImpl(final ViewHolder holder) {
}
該方法空實現(xiàn)五督,由子類負(fù)責(zé)實現(xiàn),初始化定義各種動畫初始屬性瓶殃。
關(guān)于AnimateViewHolder類充包,前面說過,可以通過自定義實現(xiàn)該接口碌燕,實現(xiàn)各種動畫效果來代替具體的ItemAnimator误证,其內(nèi)部需要實現(xiàn)的方法继薛,與繼承BaseItemAnimator的子類需要實現(xiàn)的方法一樣。
除此之外愈捅,針對add遏考,在runPendingAnimations()方法中,需要指定具體的動畫效果蓝谨。
截取runPendingAnimations()方法中的add部分灌具。
if (additionsPending) {
final ArrayList<ViewHolder> additions = new ArrayList<ViewHolder>();
additions.addAll(mPendingAdditions);
mAdditionsList.add(additions);
mPendingAdditions.clear();
Runnable adder = new Runnable() {
public void run() {
boolean removed = mAdditionsList.remove(additions);
if (!removed) {
// already canceled
return;
}
for (ViewHolder holder : additions) {
doAnimateAdd(holder);
}
additions.clear();
}
};
其中重要的一行就是:doAnimateAdd(holder);。
private void doAnimateAdd(final ViewHolder holder) {
if (holder instanceof AnimateViewHolder) {
((AnimateViewHolder) holder).animateAddImpl(holder, new DefaultAddVpaListener(holder));
} else {
animateAddImpl(holder);
}
mAddAnimations.add(holder);
}
和上面差不多譬巫,其中animateAddImpl()方法空實現(xiàn)咖楣,需要子類實現(xiàn)。
那么子類需要實現(xiàn)四個方法芦昔,就能完成自定義動畫了诱贿,是不是很簡單?
6.具體實例
下面舉一個實現(xiàn)具體動畫的例子:
public class FadeInAnimator extends BaseItemAnimator {
public FadeInAnimator() {
}
public FadeInAnimator(Interpolator interpolator) {
mInterpolator = interpolator;
}
@Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
ViewCompat.animate(holder.itemView)
.alpha(0)
.setDuration(getRemoveDuration())
.setInterpolator(mInterpolator)
.setListener(new DefaultRemoveVpaListener(holder))
.setStartDelay(getRemoveDelay(holder))
.start();
}
@Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) {
ViewCompat.setAlpha(holder.itemView, 0);
}
@Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) {
ViewCompat.animate(holder.itemView)
.alpha(1)
.setDuration(getAddDuration())
.setInterpolator(mInterpolator)
.setListener(new DefaultAddVpaListener(holder))
.setStartDelay(getAddDelay(holder))
.start();
}
}
很簡單吧咕缎,這樣我們就可以任意自定義各種增刪item的效果了珠十。
四、Adapter動畫效果實現(xiàn)原理
主要是用了裝飾者模式凭豪,對原來的adapter進行了一層封裝焙蹭,增加了額外的功能。
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);
MyAdapter adapter = new MyAdapter();
recyclerView.setAdapter(new AlphaInAnimationAdapter(adapter));
把原始的adapter傳入了具有動畫效果的新的Adpter中嫂伞,那么新的Adpter內(nèi)部是怎么實現(xiàn)的孔厉?
以AlphaInAnimationAdapter為例:
public class AlphaInAnimationAdapter extends AnimationAdapter {
private static final float DEFAULT_ALPHA_FROM = 0f;
private final float mFrom;
public AlphaInAnimationAdapter(RecyclerView.Adapter adapter) {
this(adapter, DEFAULT_ALPHA_FROM);
}
public AlphaInAnimationAdapter(RecyclerView.Adapter adapter, float from) {
super(adapter);
mFrom = from;
}
@Override protected Animator[] getAnimators(View view) {
return new Animator[] { ObjectAnimator.ofFloat(view, "alpha", mFrom, 1f) };
}
}
可見其內(nèi)部主要提供了getAnimators方法,定義動畫效果帖努,其他方法來自于AnimationAdapter撰豺。
AnimationAdapter中主要是通過onBindViewHolder來增加動畫效果
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
mAdapter.onBindViewHolder(holder, position);
int adapterPosition = holder.getAdapterPosition();
if (!isFirstOnly || adapterPosition > mLastPosition) {
for (Animator anim : getAnimators(holder.itemView)) {
anim.setDuration(mDuration).start();
anim.setInterpolator(mInterpolator);
}
mLastPosition = adapterPosition;
} else {
ViewHelper.clear(holder.itemView);
}
}
循環(huán)遍歷每個item,增加動畫效果然磷。
五郑趁、總結(jié)
通過上面的分析,對recyclerview-animators庫有了一個深刻的了解姿搜,基于此基礎(chǔ)可以實現(xiàn)各種炫酷的動畫效果寡润。
參考文章:
深入理解 RecyclerView 系列之二:ItemAnimator
RecyclerView.ItemAnimator終極解讀(一)--RecyclerView源碼解析
RecyclerView.ItemAnimator終極解讀(二)--SimpleItemAnimator和DefaultItemAnimator源碼解析
RecyclerView.ItemAnimator終極解讀(三)--繼承DefaultItemAnimator實現(xiàn)自定義動畫