这篇博客主要是用来记录博主在学习RecylcerView的自定义动画时的一些心得与遗留的疑惑,如果有写错的地方,请指出~。希望这篇博客能对大家有帮助~
首先:在观看本博客之前,需要掌握对RecyclerView的一些基础的使用,例如RecyclerView的adapter的实现等。本篇博客主要记录对于RecyclerView的自定义动画的实现,因此其他知识点上不会提太多。
话不多说先上效果:
这个自定义动画中实现了添加item时的的动画与移除item时的动画。本例能通过点击添加信息按钮和删除信息按钮添加与移除item,和通过点击item来语出RecyclerView的item。
先看移除动画的实现,老样子一步一步来,要定制自定义动画,我们需要继承SimpleItemAnimator类
在这个类中,我们需要重写它的八个方法
public class RecyclerviewAnimation extends SimpleItemAnimator{
@Override
public boolean animateRemove(RecyclerView.ViewHolder holder) {
return false;
}
@Override
public boolean animateAdd(RecyclerView.ViewHolder holder) {
return false;
}
@Override
public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
return false;
}
@Override
public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromLeft,
int fromTop, int toLeft, int toTop) {
return false;
}
@Override
public void runPendingAnimations() {
}
@Override
public void endAnimation(RecyclerView.ViewHolder item) {
}
@Override
public void endAnimations() {
}
@Override
public boolean isRunning() {
return false;
}
}
由于是实现移除item时的动画,我们需要先实现animateRemove(RecyclerView.ViewHolder holder)这个方法,这个方法在我们调用notifyItemRemoved方法时会被系统调用。
public boolean animateRemove(RecyclerView.ViewHolder holder) {
View view = holder.itemView;
view.setAlpha(1);
MyAdapter.MyHolder myHolder = (MyAdapter.MyHolder) holder;
removeAnimatorList.add(myHolder);
return true;
}
在这个方法中,我们通过传递过来的ViewHolder参数获取此次需要实行动画的View,并将ViewHolder保存在removeAnimationList中。removeAnimatorList是一个保存持有将要执行动画的View的ViewHolder的List【即removeAnimationList中添加的就是将要执行remove动画的View】。由于是一个移除动画,我们需要的动画效果是让该View渐渐消失(即alpha从1到0)。因此在动画开始之前,我们调用View的setAlpha将参数设1。
animateRemove默认返回false,我们将返回值设置为true,表示在执行完animateRemove方法之后,系统会自动帮我们调用 runPendingAnimations()方法,在runPendingAnimations方法中我们进行移除动画的播放与监听。
public void runPendingAnimations() {
for(int i = 0;i<removeAnimatorList.size();i++){
final MyAdapter.MyHolder myHolder = removeAnimatorList.get(i);
removeRunningAnimatorList.add(myHolder);
View view = myHolder.itemView;
final ViewPropertyAnimatorCompat animator = ViewCompat.animate(view);
animator.alpha(0).setDuration(1000).setListener(new ViewPropertyAnimatorListener() {
@Override
public void onAnimationStart(View view) {
}
@Override
public void onAnimationEnd(View view) {
animator.setListener(null);
view.setAlpha(0);
removeRunningAnimatorList.remove(myHolder);
dispatchRemoveFinished(myHolder);
if(!isRunning()){
dispatchAnimationsFinished();
}
}
@Override
public void onAnimationCancel(View view) {
}
}).start();
}
removeAnimatorList.clear();
}
在这个方法中,循环取出removeAnimatorList中的view,并将正在执行动画的View添加到removeRunningAnimatorList中,removeRunningAnimatorList是一个持有保存正在执行动画的View的ViewHolder的List【即removeRunningAnimatorList添加的是正在执行动画的View】,同时为每个view设置动画和监听器。
在监听到该View的动画结束时,先将View置为透明,再将该View的holder从从removeRunningAnimatorList中移除,并通过 dispatchRemoveFinished()方法告知系统remove动画播放已经结束。
isRunning方法是判断该自定义动画是不是全部结束【即没有正在进行remove动画与将要进行remove动画的view】。如果全部结束则通过 dispatchAnimationsFinished()方法告知系统动画全部结束。
在单个item的动画结束之后,系统会调用endAnimation()法,在该方法中,我们需要做的事情和监听动画结束的方法中一样,将该View的holder从removeAnimatorList中移除并且将View设置为透明。
public void endAnimation(RecyclerView.ViewHolder item) {
View view = item.itemView;
view.animate().cancel();
for (int i = 0; i < removeAnimatorList.size(); i++) {
if (removeAnimatorList.get(i) == item) {
removeAnimatorList.remove(item);
}
}
view.setAlpha(0);
dispatchRemoveFinished(item);
if (!isRunning()) {
dispatchAnimationsFinished();
}
}
最后在所有的动画结束之后,将所有要执行动画的View都设置为透明,并将其从removeAnimatorList中移除,同时将正在执行动画View的动画取消
public void endAnimations() {
for(int i = 0;i<removeAnimatorList.size();i++){
MyAdapter.MyHolder myHolder = removeAnimatorList.get(i);
View view = myHolder.itemView;
view.setAlpha(0);
removeAnimatorList.remove(myHolder);
dispatchRemoveFinished(myHolder);
}
for(int i = 0;i<removeRunningAnimatorList.size();i++){
View view = removeRunningAnimatorList.get(i).itemView;
view.animate().cancel();
}
}
isRunning方法也需要我们重写,该方法用来判断动画是否在运行,只要removeRunningAnimatorList与removeAnimatorList其中之一不为空,那么就可以认为动画在运行
public boolean isRunning() {
return !removeAnimatorList.isEmpty()||
!removeRunningAnimatorList.isEmpty();
}
然后为RecylcerView设置animator即可
RecyclerviewAnimation animation = new RecyclerviewAnimation();
recyclerView.setItemAnimator(animation);
效果
增加动画也与remove动画流程一致,只需要将View的setAlpha倒过来设置即可。这里就不再贴出代码并分析,有兴趣可以在下面的链接中下载Demo。
最后要提的是关于通过点击RecylerView的item对item进行移除。RecyclerView与ListView不同,ListView有系统写好的item点击监听供我们使用,而RecyclerView没有,因此RecyclerView的点击事件需要我们自行实现。
首先,现在RecyclerView的Adapter中设置一个接口与接口变量
private OnItemClick click;
interface OnItemClick{
void OnItemClick(int position,MyHolder holder);
}
public void setOnItemClick(OnItemClick click){
this.click = click;
}
在本例中RecyclerView的item为一个TextView。因此在为显示item进行点击监听,实际上可以设置为对TextView的点击监听。
@Override
public void onBindViewHolder(final MyHolder holder, final int position) {
holder.getTextView().setText(messageList.get(position));
if(click != null){
holder.getTextView().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
click.OnItemClick(position,holder);
}
});
}
}
最后我们只需要在Activity中为Adapter注册监听,并且实现该接口即可。
myAdapter.setOnItemClick(this);
public void OnItemClick(int position, MyAdapter.MyHolder holder) {
messageList.remove(position);
myAdapter.notifyItemRemoved(position);
Toast.makeText(MainActivity.this,"您点击的第"+(position+1)+"条数据被删除",Toast.LENGTH_SHORT).show();
}
PS:本Demo存在的问题
在多次调用notifyItemRemoved方法以后会出现个别item不显示的问题
但是在notifyItemRemoveedf方法法之后调用notifyDataSetChanged可以解决item不显示问题,但是删除不会再出现动画效果。
这个问题困扰了我很久,也搜了一些资料,有的说调用notifyItemRangeChanged方法,尝试过这个方法但是被包括在改变范围里的item都会出现移除动画的效果。如果有大神知道如何解决,希望在评论里解答一下这个疑问。
最后本例Demo的下载地址
Demo
这篇博客详细记录了作者在学习RecyclerView自定义动画时的心得,包括添加和移除item时的动画实现。博客指出,需要了解RecyclerView的基础使用才能理解自定义动画。文章通过代码示例解释了如何实现动画,并提出了在实现过程中遇到的item移除后不显示的问题,以及解决方案。此外,还提到了RecyclerView的点击事件实现方法,并分享了Demo的下载链接。
4万+

被折叠的 条评论
为什么被折叠?



