通过扩展ItemAnimator实现对Item的创建/移除动画进行抽象封装

上篇提到通过复制DefaultItemAnimator的源码来实现自定义的ItemAnimator。
本篇用来记录如何对ItemAnimator的实现进行再次的抽象封装,以适用于更加便捷的使用。

为了能够灵活定义不同的动画,我们需要把实现animator的部分抽象出来放到子类中处理。

下面以add的动画为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Override
public boolean animateAdd(final ViewHolder holder) {
resetAnimation(holder);
ViewCompat.setAlpha(holder.itemView, 0);
mPendingAdditions.add(holder);
return true;
}
private void animateAddImpl(final ViewHolder holder) {
final View view = holder.itemView;
final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
mAddAnimations.add(holder);
animation.alpha(1).setDuration(getAddDuration()).
setListener(new VpaListenerAdapter() {
@Override
public void onAnimationStart(View view) {
dispatchAddStarting(holder);
}
@Override
public void onAnimationCancel(View view) {
ViewCompat.setAlpha(view, 1);
}
@Override
public void onAnimationEnd(View view) {
animation.setListener(null);
dispatchAddFinished(holder);
mAddAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
}

我们把调整动画的时候需要修改的地方放到抽象方法中,而ItemAnimator需要对动画的监听我们放到一个定义好的Listener中供直接调用,
那么我们代码就可以变成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
protected void beforeAnimateAdd(RecyclerView.ViewHolder holder){}
@Override
public final boolean animateAdd(final RecyclerView.ViewHolder holder) {
resetAnimation(holder);
beforeAnimateAdd(holder);//调用抽象方法,让子类来提供具体实现
mPendingAdditions.add(holder);
return true;
}
protected abstract void runAnimateAdd(RecyclerView.ViewHolder holder){};
private void animateAddImpl(final RecyclerView.ViewHolder holder) {
runAnimateAdd(holder);//调用抽象方法,让子类来提供具体实现
}
protected class DefaultAddVpaListener extends VpaListenerAdapter {
RecyclerView.ViewHolder mViewHolder;
public DefaultAddVpaListener(final RecyclerView.ViewHolder holder) {
mViewHolder = holder;
}
@Override
public void onAnimationStart(View view) {
dispatchAddStarting(mViewHolder);
}
@Override
public void onAnimationCancel(View view) {
ViewHelper.clear(view);
}
@Override
public void onAnimationEnd(View view) {
ViewHelper.clear(view);
dispatchAddFinished(mViewHolder);
mAddAnimations.remove(mViewHolder);
dispatchFinishedWhenDone();
}
}

这样我们就可用把这个类定义为BaseItemAnimator,我们实现自定义动画处理器的时候继承这个类,并只实现具体的动画即可
比如这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Created by zhangfan on 16-3-13.
*/
public class LeftInAnimator extends BaseItemAnimator {
@Override
protected void beforeAnimateAdd(RecyclerView.ViewHolder holder) {
ViewCompat.setTranslationX(holder.itemView, -holder.itemView.getRootView().getWidth());
}
@Override
protected void runAnimateAdd(RecyclerView.ViewHolder holder) {
ViewCompat.animate(holder.itemView)
.translationX(0)
.setDuration(getAddDuration())
.setInterpolator(new OvershootInterpolator(1))//会让我们的动画有一个酷炫的回弹效果
.setListener(new DefaultAddVpaListener(holder))//一定不能少
.start();
}
}

相对于上篇的复制整个java文件来说,这样写无疑是更加的简便了,上面只是抽象了item的add动画,其他类型的动画同理可得。
上面的代码用到了一个叫ViewHelper.clear(view)的方法,因为我们抽出的动画监听中onAnimationCancel时我们并不知道当前执行的动画是何种类型,所以这时候定义了一个叫做ViewHelper.clear的方法来让view的所有属性都回归原始,贴上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final class ViewHelper {
public static void clear(View v) {
ViewCompat.setAlpha(v, 1);
ViewCompat.setScaleY(v, 1);
ViewCompat.setScaleX(v, 1);
ViewCompat.setTranslationY(v, 0);
ViewCompat.setTranslationX(v, 0);
ViewCompat.setRotation(v, 0);
ViewCompat.setRotationY(v, 0);
ViewCompat.setRotationX(v, 0);
ViewCompat.setPivotY(v, v.getMeasuredHeight() / 2);
ViewCompat.setPivotX(v, v.getMeasuredWidth() / 2);
ViewCompat.animate(v).setInterpolator(null).setStartDelay(0);
}
}
文章目录
,