RecyclerView已经普及使用,其各式各样的布局格式,以及众多的优越特性,使得RecyclerView具有很大的灵活性。其中之一便是ItemAnimator,通过自定义ItemAnimator可以实现各种各样的Item增加,删除,改变,移动等动画效果。这也是本篇文章的主要内容。
一、recyclerview-animators
本篇文章将围绕recyclerview-animators展开,先介绍其简单使用,然后介绍其实现原理。
首先,介绍recyclerview-animators的效果图。GitHub地址:recyclerview-animators
这个库主要实现两部分内容。
一:自定义ItemAnimator,实现Item增加和删除的动画效果。
二:对Adapter进行封装,在onBindViewHolder中,绑定过程中设置显示动画。
效果图
自定义ItemAnimator效果
![]()
Adapter动画效果
![]()
二、recyclerview-animators的使用
ItemAnimator的使用
1.引入依赖
compile 'jp.wasabeef:recyclerview-animators:2.2.6'
|
2.使用
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list)
|
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);
|
另外自定义动画实现
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.注意
使用以下方式,才会触发动画效果,具体下面分析:
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);
|
复合动画
MyAdapter adapter = new MyAdapter(); AlphaInAnimationAdapter alphaAdapter = new AlphaInAnimationAdapter(adapter); recyclerView.setAdapter(new ScaleInAnimationAdapter(alphaAdapter));
|
三、自定义ItemAnimator实现原理
上面我们分析了recyclerview-animators的实现,那么我们如何实现自己想要的更炫酷的动画方式呢,以及上述注意中为什么只能使用那几种方式才能实现动画效果呢?这一切都需要理解其原理才行,下面我们主要分析自定义动画的实现。
1.类结构
首先,所有的自定义ItemAnimator都是继承BaseItemAnimator实现的。
public abstract class BaseItemAnimator extends SimpleItemAnimator
|
而SimpleItemAnimator 又是什么? 是RecyclerView中根据ItemAnimator进行的一些简单的封装。
abstract public class SimpleItemAnimator extends RecyclerView.ItemAnimator
|
RecyclerView有着默认的动画效果实现DefaultItemAnimator,也继承了SimpleItemAnimator ,后面具体比较分析。
一切的源头都是ItemAnimator,所以先来了解下它的主要方法。
2.ItemAnimator
当RecyclerView中的item在屏幕上由可见变为不可见时调用此方法
public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
|
当RecyclerView中的item显示到屏幕上时调用此方法
public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
|
当RecyclerView中的item状态发生改变时调用此方法(notifyItemChanged(position))
public abstract boolean animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
|
统筹RecyclerView中所有的动画,统一启动执行
abstract public void runPendingAnimations();
|
这几个方法是自定义ItemAnimator的关键,实现不同的动画效果。
SimpleItemAnimator 在ItemAnimator的基础上,针对上述几个方法,进行了一定的封装,使得可以更专注于动画的实现。
3.SimpleItemAnimator
SimpleItemAnimator对这几个方法是如何封装的呢?
以添加一个Item举例,最后调用的是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坐标是否相等 如果不相等则调用animateMove,否则调用animateAdd(viewHolder)方法。
其他方法类似,最后我们只需要实现animateRemove, animateAdd, animateMove, animateChange 这4个方法。
下面我们先看看RecyclerView中DefaultItemAnimator的实现,再来分析BaseItemAnimator。
4.DefaultItemAnimator
首先是对animateRemove, animateAdd, animateMove, animateChange 这4个方法的实现。
还是以animateAdd为例:
public boolean animateAdd(final ViewHolder holder) { resetAnimation(holder); ViewCompat.setAlpha(holder.itemView, 0); mPendingAdditions.add(holder); return true; }
|
首先调用resetAnimation(holder)停止此holder中itemView的动画效果,然后将holder.itemView的透明度设置为0不可见状态(也可以设置其他属性,相当于动画开始的初始效果)。
将holder添加到mPendingAdditions集合中。
注意:此集合一会儿会在runPendingAnimations方法中进行遍历执行动画。
然后是对runPendingAnimations的实现。
下面上源码,很长,不过别害怕,很简单。
public void runPendingAnimations() { boolean removalsPending = !mPendingRemovals.isEmpty(); boolean movesPending = !mPendingMoves.isEmpty(); boolean changesPending = !mPendingChanges.isEmpty(); boolean additionsPending = !mPendingAdditions.isEmpty(); if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
|
remove最先执行,for循环遍历mPendingRemovals集合中所有的ViewHolder。remove完成后,再同时开始move和change动画,最后执行add动画。
其中,需要注意几个重要的方法:
animateRemoveImpl(holder);
animateMoveImpl(moveInfo.holder,moveInfo.fromX, moveInfo.fromY,moveInfo.toX, moveInfo.toY);
animateChangeImpl(change);
animateAddImpl(holder);
这几个方法执行具体的动画效果,具体来看源码。
下面来分析下BaseItemAnimator的实现。
5.BaseItemAnimator
了解了DefaultItemAnimator,那么对BaseItemAnimator的实现就清楚了。
首先BaseItemAnimator使用了DefaultItemAnimator中的runPendingAnimations()方法的实现。
其次BaseItemAnimator使用了DefaultItemAnimator中的animateMove, animateChange,以及animateRemoveImpl,animateChangeImpl等方法的实现。
所以BaseItemAnimator只留下了animateRemove, animateAdd方法,方便我们自定义实现。
以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类或子类,那么就调用其preAnimateAddImpl()方法,否则调用内部的preAnimateAddImpl()方法。
protected void preAnimateAddImpl(final ViewHolder holder) { }
|
该方法空实现,由子类负责实现,初始化定义各种动画初始属性。
关于AnimateViewHolder类,前面说过,可以通过自定义实现该接口,实现各种动画效果来代替具体的ItemAnimator,其内部需要实现的方法,与继承BaseItemAnimator的子类需要实现的方法一样。
除此之外,针对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) {
|
其中重要的一行就是: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()方法空实现,需要子类实现。
那么子类需要实现四个方法,就能完成自定义动画了,是不是很简单?
6.具体实例
下面举一个实现具体动画的例子:
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动画效果实现原理
主要是用了装饰者模式,对原来的adapter进行了一层封装,增加了额外的功能。
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list); MyAdapter adapter = new MyAdapter(); recyclerView.setAdapter(new AlphaInAnimationAdapter(adapter));
|
把原始的adapter传入了具有动画效果的新的Adpter中,那么新的Adpter内部是怎么实现的?
以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) }; } }
|
可见其内部主要提供了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); } }
|
循环遍历每个item,增加动画效果。
五、总结
通过上面的分析,对recyclerview-animators库有了一个深刻的了解,基于此基础可以实现各种炫酷的动画效果。
参考文章:
深入理解 RecyclerView 系列之二:ItemAnimator
RecyclerView.ItemAnimator终极解读(一)–RecyclerView源码解析
RecyclerView.ItemAnimator终极解读(二)–SimpleItemAnimator和DefaultItemAnimator源码解析
RecyclerView.ItemAnimator终极解读(三)–继承DefaultItemAnimator实现自定义动画