之前我们做点赞,用listview做的话,就是在item实现点击后,写一个scal动画,不过现在都转到RecyclerView,那么要做这种效果于是做了一个notifyItemChanged()的操作,功能都顺利实现,美中不足的是当前Item闪了一下,QA甚至为此提了Bug,一开始以为是图片加载库的问题,之后随着图片加载库从ImageLoader换成Picaso,又换成Glide,这个Bug一直如影随形。后来才发现“闪一下”原来是RecyclerView的默认动画,我的代码里有这样一句mRecyclerView.setItemAnimator(new
原来是这句代码搞的鬼,于是注掉再跑,但并没什么卵用。于是又改成
DefaultItemAnimator());mRecyclerView.setItemAnimator(null);
仍然没什么卵用。看来加不加这句,RecyclerView都默认执行了这个动画,看来还有点小麻烦。
before.gif
先来了解下这个RecyclerView的动画吧。
RecyclerView.ItemAnimator
ItemAnimator能够帮助Item实现独立的动画。
ItemAnimator触发于以下三种事件:
某条数据被插入到数据集合中
从数据集合中移除某条数据
更改数据集合中的某条数据
在Android中默认实现了一个DefaultItemAnimator
,我们可以通过以下代码为Item增加动画效果:recyclerView.setItemAnimator(new DefaultItemAnimator());
在之前的版本中,当数据集合发生改变时,我们通过调用notifyDataSetChanged()
来刷新列表,因为这样做会触发列表的重绘,所以并不会出现任何动画效果,但现在我的需求是只改变了当前一个Item的状态,因此需要调用一些以notifyItem*()
作为前缀的特殊方法,比如:
向指定位置插入Itempublic final void notifyItemInserted(int position)
移除指定位置Itempublic final void notifyItemRemoved(int position)
更新指定位置Itempublic final void notifyItemChanged(int position)
但是现在的问题就是,调用notifyItem*()方法会触发RecyclerView的默认动画,而这个动画我并不想要,但是似乎并没有合适的办法来屏蔽这个动画,QA甚至认为这是个Bug,需要修复。与同行们交流了下,也有人遇到同样的问题,解决办法居然是调用notifyDataSetChanged()方法来刷新数据,这样就不会有闪一下的动画了。但是这样不就失去使用RecyclerView的优势和意义了吗?
最后我是通过重写RecyclerView的动画来解决这一“Bug”。
public class NoAlphaItemAnimator extends RecyclerView.ItemAnimator {
}
将DefaultItemAnimator类里的代码全部copy到自己写的动画类中,然后做一些修改。
首先找到private void animateChangeImpl(final ChangeInfo changeInfo) {}
方法。
找到方法里这两句代码:
1:oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() { ... }
2:newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).alpha(1).setListener(new VpaListenerAdapter() { ... }
替换成:
1:oldViewAnim.setListener(new VpaListenerAdapter() { ... }
2newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).setListener(new VpaListenerAdapter() { ... }
也就是说分别去掉上述代码中的alpha(0)
和alpha(1)
,然后保存。这个时候基本就打工告成了。最后在自己的RecyclerView中进行如下调用mRecyclerView.setItemAnimator(new NoAlphaItemAnimator());
,再跑起来,bug完美解决。