I believe everyone is RecyclerView
familiar with the usage, and RecyclerView
the advent of the show gives us the developer a highly extended control,
Whether it's a list, grid, waterfall flow, a control can be done, and the magic is 只需要修改一行代码,就可以轻松切换
. Too many benefits too much RecyclerView
, do not list, there are a lot RecyclerView
of tutorials on the web. Speaking of this, we began to enter the theme, although there are so many tutorials on the web, RecyclerView
but not a detailed description RecyclerView
of the animation, most of the use of the default DefaultItemAnimator
or use a third-party animation library, this blog we will make up this gap, we come to the basis DefaultItemAnimator
of Code to implement a simple RecyclerView
animation.
We just go to achieve the most commonly used add
and remove
animation, other animations, if you are interested, you can refer DefaultItemAnimator
to the extension.
Before we start, let's take a look at the effect of the implementation:
How do I customize the animation? The first thing to inherit from this class is that since we are RecyclerView.ItemAnimator
going to inherit this class, it is necessary to look at the class and some of the methods in this class (just focus on what we need today, other similarities).
This class defines the animations, the take place on items as changes is made to the adapter. Subclasses of Itemanimator can is used to implement custom animations for actions on viewholder items. The Recyclerview would manage retaining these items while they is being animated, but Implementors must call the Appropria TE "Starting" (Dispatchremovestarting (Viewholder), dispatchmovestarting (Viewholder), dispatchchangestarting ( Viewholder, Boolean), or dispatchaddstarting (Viewholder)) and "Finished" (Dispatchremovefinished (Viewholder), Dispatchmovefinished (Viewholder), Dispatchchangefinished (Viewholder, Boolean), or dispatchaddfinished (ViewHolder)) Methods when the item animation is being started and ended.
Just look at the focus, when the animation starts, we need to call dispatchXXXStarting()
, when the animation ends, we need to call dispatchXXXFinished()
.
Next, take a look at some of the ways we need to do this:
-
IsRunning ()
Returns whether there is currently an animation to be performed.
-
runpendinganimations ()
Called when an animation is to be performed. It is important to note that when we go to add an item, the animation may not be executed immediately, and this mechanism allows Itemanimator to add and then execute it.
-
Animateadd ()
Add animation when we call Adapter.notifyiteminsert () fires the method, which has a Boolean return value that indicates whether runpendinganimations
can be executed at the next opportunity. So when we customize the animation, this method returns True.
-
Similar to Animateadd
also has animatemove
, animateremove
, Animatechange
.
-
dispatchaddstarting ()
is called at the beginning of the animation.
-
dispatchaddfinished ()
Animation at the end of the call.
-
dispatchanimationsfinished ()
All animations are called at the end.
Well, the introduction of a few uses of the method, the following to achieve our own animation bar. Or the animation above that we only implement add
and remove
. DefaultItemAnimator
we need 4 of them to imitate the calculations ArrayList
.
private ArrayList<RecyclerView.ViewHolder> mPendingAddHolders = new ArrayList<>();private ArrayList<RecyclerView.ViewHolder> mPendingRemoveHolders = new ArrayList<>();privatenew ArrayList<>();privatenew ArrayList<>();
Clearly on the two animation, how to need 4? ArrayList
And it's a pair! Envy not? This is a good place to say. It says, the animation may not be executed immediately, but in a runPendingAnimations
piece to execute, so we are in animateAdd
, just mPendingAddHolders
add one to the ViewHolder
, not to write the animation code. At this point, consider a situation where:
When we animateAdd
had a time when runPendingAnimations
the animation was not finished, so we can not clear mPendingAddHolders
the collection, then the execution of a time animateAdd
what will happen? The previous one repeated the animation, in the DefaultItemAnimator
smart to solve the problem, and the above 4 variables, in the following code, we will also learn this way to achieve.
Before starting the code, let's take isRunning
a look at how this method should be written.
@OverridepublicbooleanisRunning() { return !(mPendingAddHolders.isEmpty() && mPendingRemoveHolders.isEmpty() && mAddAnimtions.isEmpty() && mRemoveAnimations.isEmpty());}
Not much to say, only a word: isRunning
not that there is no animation to do it.
That continues the code, to look at animateAdd
and animateRemove
How the method is written.
@OverridepublicbooleananimateAdd(RecyclerView.ViewHolder holder) { holder.itemView.setAlpha(0.f); mPendingAddHolders.add(holder); returntrue;}@OverridepublicbooleananimateRemove(RecyclerView.ViewHolder holder) { mPendingRemoveHolders.add(holder); returntrue;}
It says, here we simply add a holder to the collection and set the return value to true
be executed runPendingAnimations
. It is important to note that the animateAdd
first line of code for the method, we will not see the view settings, so that the purpose is to prevent the item flashing (before the animation is performed).
Then there's the next thing: runPendingAnimations
:
@Override Public void runpendinganimations() {BooleanIsremove =!mpendingremoveholders.isempty ();BooleanIsadd =!mpendingaddholders.isempty ();if(!isremove &&!isadd)return;//First remove if(Isremove) { for(Recyclerview.viewholder holder:mpendingremoveholders) {Animateremoveimpl (holder); } mpendingremoveholders.clear (); }//Last add if(Isadd) {Arraylist<recyclerview.viewholder> holders =NewArraylist<> (); Holders.addall (mpendingaddholders); Mpendingaddholders.clear (); for(Recyclerview.viewholder holder:holders) {Animateaddimpl (holder); } holders.clear (); }}
To explain the code, first two variables to determine whether the two pending collection is not empty, this determines whether our code needs to be executed down. And then to Judge isRemove
, in this, to perform remove
the animation,
As you can see, we iterate through each of the saved Viewholder, then go to execute the animateRemoveImpl
method and finally mPendingRemoveHolders
empty.
animateRemoveImpl
Let's go ahead and take a look add
, here we first mPendingAddHolders
add all the holder to a local list, then empty it,
The purpose of this is to prevent the animation from being repeated, and then, as with remove, to traverse all the holder execution animateAddImpl
methods, and finally, to empty the local list.
And then, we're going to look at the animateRemoveImpl
animateAddImpl
method, and these two methods are the place to actually do the animation.
//Perform add animationPrivate void Animateaddimpl(FinalRecyclerview.viewholder holder) {maddanimtions.add (holder);FinalView item = Holder.itemview; Objectanimator animator = Objectanimator.offloat (item,"Alpha",0.F1.f); Animator.setduration ( +); Animator.addlistener (NewAnimatorlisteneradapter () {@Override Public void Onanimationstart(Animator animation) {dispatchaddstarting (holder); }@Override Public void Onanimationcancel(Animator animation) {Item.setalpha (1.f); }@Override Public void Onanimationend(Animator animation) {dispatchaddfinished (holder); Maddanimtions.remove (holder);if(!isrunning ()) dispatchanimationsfinished (); } }); Animator.start ();}//Perform a move-out animationPrivate void Animateremoveimpl(FinalRecyclerview.viewholder holder) {mremoveanimations.add (holder);FinalView item = Holder.itemview; Objectanimator animator = Objectanimator.offloat (item,"Alpha",1.F0.f); Animator.setduration ( +); Animator.addlistener (NewAnimatorlisteneradapter () {@Override Public void Onanimationstart(Animator animation) {dispatchremovestarting (holder); }@Override Public void Onanimationend(Animator animation) {mremoveanimations.remove (holder); Item.setalpha (1.f); dispatchremovefinished (holder);if(!isrunning ()) dispatchanimationsfinished (); } }); Animator.start ();}
You can see that these two methods are very similar, so we just say one of them, well, just say the one that's closest to me animateRemoveImpl
.
First add this holder to mRemoveAnimations
, and then a very familiar property animation, here we just change the value of Itemview in the animation, alpha
We call the method at the beginning of the animation in the document, dispatchRemoveStarting
at the end of the animation call dispatchRemoveFinished
method, and finally to determine whether there is no execution of the animation,
If not, call dispatchAddFinished
. Everything that is done here is in accordance with the rules of the document.
So excited, finally realized RecyclerView
the animation, to use our Itemanimator
...mRecyclerView.setAdapter(mAdapter);mRecyclerView.setItemAnimator(new MyItemAnimator());
Look at the effect:
From the effect can be seen, our add
animation is no problem, but the remove
animation cliff is not the effect we want!! It's too ... it.
Well, after careful observation of the 1min effect, I finally found that the problem lies, secretly told you:
When we remove, does the following item move up? Moved, is that the way to execute it animateMove
?
So, we also need move
a set of processes.
First, add a pair of sets.
private ArrayList<MoveInfo> mPendingMoveHolders = new ArrayList<>();privatenew ArrayList<>();
Another pair of good friends, oh, no, it's a good couple.
Hey MoveInfo
, what's this? Think carefully about a moving process that is not needed to know 来自哪里,将要去何方
. We imitate DefaultItemAnimator
to define an inner class MoveInfo
.
Class Moveinfo {PrivateRecyclerview.viewholder Holder;Private intFromX;Private intFromY;Private intToX;Private intToY; Public Moveinfo(Recyclerview.viewholder holder,intFromX,intFromY,intToX,intToY) { This. Holder = Holder; This. FromX = FromX; This. FromY = FromY; This. ToX = ToX; This. ToY = ToY; }}
Well, there is nothing to say, then continue to look at the animateMove
method, it must be added to the mPendingMoveHolders
one, but here is the addition of one MoveInfo
.
@Override public boolean Animatemove (recyclerview.viewholder holder, int FromX, int FromY, int ToX, int ToY) {View view = Holder.itemview; FromY + = View.gettranslationy (); int delta = toy-fromy; View.settranslationy (-delta); Moveinfo info = new moveinfo (Holder, FromX, FromY, ToX, ToY); Mpendingmoveholders.add (info); return true ;}
It is worth mentioning that int delta = toY - fromY
we have to calculate the distance to change the view to move, and then take the reverse plug view.translationY
,
The purpose of this is to get the top one in the process of moving out, the following item does not move up immediately. Instead, offset a certain distance.
Here for good understanding, we print a set of values to help me understand
FROMY:20, toy:0
Delta:-20
Translationy:20
So in this scenario, you first move the view down by 20 pixels, and the effect is that it does not move in its original position.
Go down, construct one MoveInfo
and add it to the mPendingMoveHolders
inside.
Continue to modify the runPendingAnimations
method, add our move operation, this part add
of the code is very similar to the code, can be copied to modify the changes.
@Override Public void runpendinganimations() {BooleanIsremove =!mpendingremoveholders.isempty ();BooleanIsmove =!mpendingmoveholders.isempty ();BooleanIsadd =!mpendingaddholders.isempty ();if(!isremove &&!ismove &&!isadd)return; ...//Then move if(Ismove) {arraylist<moveinfo> Infos =NewArraylist<> (); Infos.addall (mpendingmoveholders); Mpendingmoveholders.clear (); for(Moveinfo Info:infos) {Animatemoveimpl (info); } infos.clear (); } ...//Last add}
You can see that our move is completely a copy of the add, if you do not understand, you can turn online blog, see the Add section of the description.
Continue to come to the animateMoveImpl
method.
//Perform motion animationsPrivate void Animatemoveimpl(FinalMoveinfo info) {Mmoveanimtions.remove (info);FinalView view = Info.holder.itemView; Objectanimator animator = objectanimator.offloat (view,"Translationy", View.gettranslationy (),0); Animator.setduration ( +); Animator.addlistener (NewAnimatorlisteneradapter () {@Override Public void Onanimationstart(Animator animation) {dispatchmovestarting (Info.holder); }@Override Public void Onanimationend(Animator animation) {dispatchmovefinished (Info.holder); Mmoveanimtions.remove (Info.holder);if(!isrunning ()) dispatchanimationsfinished (); } }); Animator.start ();}
In this we construct an translationY
animation, the effect is from the view current offset to 0 of a constant offset effect. To put it bluntly is the effect of continuous upward. The processing and add are the same logic at the beginning and end of the animation.
Now the code is finally finished, the effect is the beginning of the blog effect.
Said, to the present we only have not achieved the effect of the effect is change
, in fact, the change
processing and move
processing is very similar,
Interested friends can refer to DefaultItemAnimator
the source code.
It seems that it is RecyclerView
not so complicated to implement an item animation yourself, but there is a lot of code, is it not that we need to write this long code every time? Of course not! Watch the code carefully,
In fact, the implementation of the animation is animateXXXImpl
implemented in, we can completely aninateXXXImpl
abstract out, need what animation, we inherit this class, just to achieve aninateXXXImpl
the
Code can, of course, others have provided us with a lot of animation library, we can not write on their own, directly use the three-side. Here's a great RecyclerView
library of animations to do,
Direct copy to the project can be used.
Recyclerview Item Animation Library on GitHub
At the end of the demo:
Demo Download, Poke here
Note: The property animation in the demo, if you need backward compatibility, can be swapped nineoldandroids
or ViewCompat
implemented.
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Advanced usage of Recyclerview-custom animations