Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析
这是关于RecyclerView的第二篇,说的是如何自定义Item动画,但是请注意,本文不包含动画的具体实现方法,只是告诉大家如何去自定义动画,如何去参考源代码。
我们知道,RecyclerView默认会使用DefaultItemAnimator,所以如果我们需要自定义动画,那么应该好好的读读这个类的源代码,这样不仅仅是学习怎么自定义,还要学习Android的设计模式。
先弄明白一件事,DefaultItemAnimator继承自SimpleItemAnimator,SimpleItemAnimator继承自RecyclerView.ItemAnimator,所以如果需要自定义动画,最简单的方法是继承SimpleItemAnimator。其次,动画的类型有四种,分别是Add、Remove、Move以及Change,这里我们只列举Remove,举一反三。
我们先看SimpleItemAnimator中的源码,在SimpleItemAnimator中有几个重要的方法:
@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);
}
}
解析:这个函数是重写RecyclerView.ItemAnimator的,接口中参数分别是ViewHolder、prelayoutInfo以及postLayoutInfo,第一个参数是指Item的ViewHolder,可以通过这个对象的itemView来获取它的View,第二个参数是指Item删除前的位置信息,第三个是指新的位置信息。再接下来会判断ViewHolder是否已经被移除以及位置是否发生变化,然后在调用animateRemove这个抽象方法,如果我们要自定义动画,就需要去实现它(回调思想)。
public final void dispatchRemoveStarting(ViewHolder item) {
onRemoveStarting(item);
}
public void onRemoveStarting(ViewHolder item) {
}
解析:dispatchRemoveStaring个是一个final方法,也就是不能被重写,如果我们需要处理一些在Remove开始的时候的逻辑,我们就需要在animateRemove方法中调用这个方法,这个方法会执行一个onRemoveStaring方法,这个方法就允许我们重写,所以逻辑应该写在onRemoveStaring中,当我们调用dispatchRemoveStaring的时候,onRemoveStaring就会被执行。
这里只说了两个,但是,加上其他动作的就不只是两个啦。。。
所以,当我们继承了SimpleItemAnimator的时候,需要实现里面的一些方法,一般有如下这些:
① animateRemove(Add、Move和Change):这些方法会在动画发生的时候回调,一般会在这个方法中用列表记录每个Item的动画以及属性
② endAnimation、endAnimations:分别是在一个Item或是多个Item需要立即停止的时候回调
③ isRunning:如果需要顺畅滑动的时候,必须要重写这个方法,很多时候比如在网络加载的时候滑动卡顿就是这个方法逻辑不对
④ run'PendingAnimations:这是最重要的一个方法。因为animateDisappearence等方法返回的是animateRemove等方法返回的值,而这个方法则是根据这些值来确定是否有准备好的动画需要播放,如果有,就会回调这个方法。在这个方法我们需要处理每一个动作(Remove、Add、Move以及Change)的动画
所以,我们的一般步骤就是:
①创建一个SimpleItemAnimator的子类
②创建每个动作的动作列表
③重写animateRemove等方法,当界面中有动作发生,这些函数会被回调,这里进行记录并返回true使得run'PendingAnimations开始执行
④重写run'PendingAnimations,当③的方法返回true的时候,就认为需要执行动画,我们需要把动画执行的逻辑写在这里面
⑤重写isRunning,提供动画播放状态,一般是返回动作列表是否为空
⑥如果有需要,重写endAnimation、endAnimations、onRemoveFinish等方法
具体的步骤有了,但是我们还不清楚该怎么构建它,不用着急,为了方便我们,谷歌其实已经提供了DefaultItemAnimator,我们可以参考一些它的源码,没有人讲的比源码有道理,我们需要的是有足够的耐心!
DefaultItemAnimator中定义了一些ArrayList来存放动作的信息,如下:
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
......
}
解析:根据上面可以知道,runPendingAnimations会执行,可看到,在这个方法中遍历了动作列表,并让每个Item都执行了animatorRemoveImpl方法,其他动作的方法暂时先省略,有兴趣的可以自行阅读。
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方法中实现了整个动画的具体逻辑,具体怎么做不在本文范围中,在我们执行了动画之后,也就是在动画的Listener中的onAnimatorEnd中调用了dispatchRemoveFinish,还记得这个方法吗,它会执行onRemoveFinish方法,onRemoveFinish方法是可以供给我们重写的。然后把item移除动作列表。
@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方法其实就是根据动作列表是否为空来返回结果
还有其他一些函数可以自己阅读源代码。
Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析的更多相关文章
- Android开发学习之路-RecyclerView滑动删除和拖动排序
Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...
- Android开发学习之路-RecyclerView使用初探
在进行一些MaterialDesign规范开发的时候,比如之前说到的CoordinateLayout实现的向上折叠效果的时候,如果依然使用ListView,那么这种效果是做不出来的,因为ListVie ...
- Android开发学习之路--网络编程之xml、json
一般网络数据通过http来get,post,那么其中的数据不可能杂乱无章,比如我要post一段数据,肯定是要有一定的格式,协议的.常用的就是xml和json了.在此先要搭建个简单的服务器吧,首先呢下载 ...
- Android开发学习之路--Activity之初体验
环境也搭建好了,android系统也基本了解了,那么接下来就可以开始学习android开发了,相信这么学下去肯定可以把android开发学习好的,再加上时而再温故下linux下的知识,看看androi ...
- Android开发学习之路--基于vitamio的视频播放器(二)
终于把该忙的事情都忙得差不多了,接下来又可以开始good good study,day day up了.在Android开发学习之路–基于vitamio的视频播放器(一)中,主要讲了播放器的界面的 ...
- Android开发学习之路--Android Studio cmake编译ffmpeg
最新的android studio2.2引入了cmake可以很好地实现ndk的编写.这里使用最新的方式,对于以前的android下的ndk编译什么的可以参考之前的文章:Android开发学习之路– ...
- Android开发学习之路--Android系统架构初探
环境搭建好了,最简单的app也运行过了,那么app到底是怎么运行在手机上的,手机又到底怎么能运行这些应用,一堆的电子元器件最后可以运行这么美妙的界面,在此还是需要好好研究研究.这里从芯片及硬件模块-& ...
- Android开发学习之路--MAC下Android Studio开发环境搭建
自从毕业开始到现在还没有系统地学习android应用的开发,之前一直都是做些底层的驱动,以及linux上的c开发.虽然写过几个简单的app,也对android4.0.3的源代码做过部分的分析,也算入门 ...
- Android源码浅析(四)——我在Android开发中常用到的adb命令,Linux命令,源码编译命令
Android源码浅析(四)--我在Android开发中常用到的adb命令,Linux命令,源码编译命令 我自己平时开发的时候积累的一些命令,希望对你有所帮助 adb是什么?: adb的全称为Andr ...
随机推荐
- spring spring data jpa save操作事务
整合spring spring data jpa的时候,在save方法上加了@Transactional注解.此时调用springdatajpa save方法并不会真的把数据提交给数据库,而是缓存起来 ...
- Blend打不开wpf项目,提示无法识别的工具版本“12.0”
讲项目文件用记事本打开然后修改为“14.0”即可
- 求单链表L各结点的阶乘之和(c语言)
链表需要用到指针 阶乘需要用到递归 链表中的注意事项: 1.链表L是否等于NULL ----------是循环结束的条件 2.链表L->Data ---------取链表L中各个结点的值 3.L ...
- apt 根据注解,编译时生成代码
apt: @Retention后面的值,设置的为CLASS,说明就是编译时动态处理的.一般这类注解会在编译的时候,根据注解标识,动态生成一些类或者生成一些xml都可以,在运行时期,这类注解是没有的~~ ...
- dom4j解析示例
收藏信息.xml <?xml version="1.0" encoding="GB2312" standalone="no"?> ...
- 将库打包到nuget服务器
前言 由于目前.net core无法引用本地的 assembly ,所以尝试将项目的依赖库打包到nuget服务器. 相关的工具 Nuget Package Explorer 参考文档 dotnet ...
- 在mvc里面有htmlhelper方法,在webform里面有什么?
终于是找到原来在webform里面已经提供了htmlcontrol这样的控件,可以直接拿来用.以前一直在想mvc有htmlhelper,webform里面不能用,其实是webform里面已经有了. 例 ...
- swift-闭包(代码块)
语法 通用的语法 {(parameters) -> return type in statements } e.g let studname = { println("Welcome ...
- SQLServer触发器创建、删除、修改、查看
一: 触发器是一种特殊的存储过程﹐它不能被显式地调用﹐而是在往表中插入记录﹑更新记录或者删除记录时被自动地激活.所以触发器可以用来实现对表实施复杂的完整性约束. 二: SQL Server为每个触发器 ...
- Html5+Css3制作下拉菜单的三种方式
一.渐变式改变ol的高度 1.外部为ul标签,在每个li里嵌套一个ol列表2.设置外部li左浮动,内部ol标签绝对定位,外部li标签相对定位3.设置ol的高为0,溢出隐藏4.外部li标签:hover ...