Advanced usage of RecyclerView-Custom Animation and recyclerview
I believe everyone is rightRecyclerView
The usage is quite familiar,RecyclerView
We provide developers with a highly scalable control,
Whether it's a list, a grid, or a waterfall stream, a widget can do it, and what's amazing isYou only need to modify a line of code to easily switch
.RecyclerView
There are too many benefits, so I will not list them one by one. There are also many onlineRecyclerView
. Speaking of this, we started to enter the topic, although there are so manyRecyclerView
But none of them are described in detail.RecyclerView
Animation, most of which use the defaultDefaultItemAnimator
Or use a third-party animation library. This blog is used to make up for this blank space.DefaultItemAnimator
Code to implement a simpleRecyclerView
Animation.
Let's just implement the most commonadd
Andremove
If you are interested, you can referDefaultItemAnimator
To expand.
Before starting, let's take a look at the implementation results:
How can we customize animations? First, you must inherit fromRecyclerView.ItemAnimator
To inherit this class, it is necessary to understand the class and several methods in this class (only focus on what we need today, and other classes are the same ).
This class defines the animations that take place on items as changes are made to the adapter. subclasses of ItemAnimator can be used to implement anim animations for actions on ViewHolder items. the RecyclerView will manage retaining these items while they are being animated, but implementors must call the appropriate "Starting" (values (ViewHolder), values (ViewHolder), values (ViewHolder, boolean ), or dispatchAddStarting (ViewHolder) and "Finished" (dispatchRemoveFinished (ViewHolder), destroy (ViewHolder), destroy (ViewHolder, boolean), or dispatchAddFinished (ViewHolder )) methods when each item animation is being started and ended.
Focus only. when starting the animation, we need to calldispatchXXXStarting()
When the animation ends, we need to calldispatchXXXFinished()
.
Next, let's take a look at the several methods we need to implement:
Now, we have introduced several methods to use. Let's start to implement our own animation. As mentioned above, we only implementadd
Andremove
Animation. ImitatingDefaultItemAnimator
In this case, we need fourArrayList
.
private ArrayList<RecyclerView.ViewHolder> mPendingAddHolders = new ArrayList<>();private ArrayList<RecyclerView.ViewHolder> mPendingRemoveHolders = new ArrayList<>();private ArrayList<RecyclerView.ViewHolder> mAddAnimtions = new ArrayList<>();private ArrayList<RecyclerView.ViewHolder> mRemoveAnimations = new ArrayList<>();
Obviously there are only two animations. How do I need fourArrayList
What about it? And a pair! Envy? Let's talk about it. As mentioned above, the animation may not be executed immediately, but inrunPendingAnimations
One piece to execute, so weanimateAdd
Only forwardmPendingAddHolders
AddedViewHolder
Instead of writing the animation code. In this case, consider the following:
When weanimateAdd
Once, thenrunPendingAnimations
The animation is not completed yet, so we cannot clear it.mPendingAddHolders
This set is executed again.animateAdd
What will happen? The previous animation was repeatedly executed.DefaultItemAnimator
It is related to the above four variables. We will also use this method for implementation in the following code.
Before starting the code, let's take a look.isRunning
How to write this method.
@Overridepublic boolean isRunning() { return !(mPendingAddHolders.isEmpty() && mPendingRemoveHolders.isEmpty() && mAddAnimtions.isEmpty() && mRemoveAnimations.isEmpty());}
Not to mention, there is only one sentence:isRunning
Does it mean there is no animation to be executed.
Let's continue with the code.animateAdd
AndanimateRemove
Method.
@Overridepublic boolean animateAdd(RecyclerView.ViewHolder holder) { holder.itemView.setAlpha(0.f); mPendingAddHolders.add(holder); return true;}@Overridepublic boolean animateRemove(RecyclerView.ViewHolder holder) { mPendingRemoveHolders.add(holder); return true;}
As mentioned above, here we just add a holder to the set and set the return valuetrue
, Indicates that the task can be executed.runPendingAnimations
. Note thatanimateAdd
The first line of code of the method, we set the view to invisible, so as to prevent the item from flashing (the animation is executed only after it appears ).
The next step is the major event:runPendingAnimations
:
@Overridepublic void runPendingAnimations() { boolean isRemove = !mPendingRemoveHolders.isEmpty(); boolean isAdd = !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 = new ArrayList<>(); holders.addAll(mPendingAddHolders); mPendingAddHolders.clear(); for(RecyclerView.ViewHolder holder : holders) { animateAddImpl(holder); } holders.clear(); }}
To explain the code, we first need two variables to determine whether the two pending sets are not empty. This determines whether our code needs to be executed. Then judgeisRemove
To executeremove
Animation,
We can see that we traverse every saved ViewHolder and then executeanimateRemoveImpl
Method.mPendingRemoveHolders
Empty.
animateRemoveImpl
Let's leave it alone and continue looking at it.add
FirstmPendingAddHolders
Add all holder in to a local List, and then empty,
The purpose of this operation is to prevent the animation from being repeatedly executed, and then traverse all holder executions in the same way as when removing the animation.animateAddImpl
Method. Finally, the local list is cleared.
Next, let's take a look.animateRemoveImpl
AndanimateAddImpl
The two methods are the place where the animation is actually executed.
// Execute the add animation private void animateAddImpl (final RecyclerView. viewHolder holder) {mAddAnimtions. add (holder); final View item = holder. itemView; ObjectAnimator animator = ObjectAnimator. ofFloat (item, "alpha", 0.f, 1.f); animator. setDuration (1000); animator. addListener (new AnimatorListenerAdapter () {@ Override public void onAnimationStart (Animator animation) {dispatchAddStarting (holder);} @ Override publ Ic void onAnimationCancel (Animator animation) {item. setAlpha (1.f) ;}@ Override public void onAnimationEnd (Animator animation) {conditions (holder); mAddAnimtions. remove (holder); if (! IsRunning () dispatchAnimationsFinished () ;}}); animator. start ();} // execute the remove animation private void animateRemoveImpl (final RecyclerView. viewHolder holder) {mRemoveAnimations. add (holder); final View item = holder. itemView; ObjectAnimator animator = ObjectAnimator. ofFloat (item, "alpha", 1.f, 0.f); animator. setDuration (1000); animator. addListener (new AnimatorListenerAdapter () {@ Override public void onAnimatio NStart (Animator animation) {dispatchRemoveStarting (holder) ;}@ Override public void onAnimationEnd (Animator animation) {mRemoveAnimations. remove (holder); item. setAlpha (1.f); dispatchRemoveFinished (holder); if (! IsRunning () dispatchAnimationsFinished () ;}}); animator. start ();}
We can see that these two methods are very similar, so we only talk about one of them, well, that is the one closest to me.animateRemoveImpl
Right.
Add this holdermRemoveAnimations
And then an attribute animation that everyone is very familiar with. Here we only change the attributes of itemView In the animation.alpha
Value,
As described in the document, we call this function at the beginning of an animation.dispatchRemoveStarting
Method, called at the end of the animationdispatchRemoveFinished
Finally, we determined whether the animation was executed,
If not, calldispatchAddFinished
. Everything is done here according to the provisions of the document.
I'm so excited that I finally achieved it.RecyclerView
To use our ItemAnimator.
...mRecyclerView.setAdapter(mAdapter);mRecyclerView.setItemAnimator(new MyItemAnimator());
See the results:
We can see from the results that ouradd
Animation is fine, but Nimaremove
The animation is not what we want !! This is too... Now.
Well, after carefully observing the effect for 1 min, I finally found out the problem and secretly told you:
When we remove it, did the following items move up? If it is moved, will it be executed?animateMove
Method?
Therefore, we also need a setmove
.
First, add a set.
private ArrayList<MoveInfo> mPendingMoveHolders = new ArrayList<>();private ArrayList<MoveInfo> mMoveAnimtions = new ArrayList<>();
It's a good friend. Oh, no, it's a good couple.
Ah? ThisMoveInfo
What is it? Think about it. Do you need to know the process of moving?Where is it from and where is it going?
. We mimicDefaultItemAnimator
To define an internal classMoveInfo
.
class MoveInfo { private RecyclerView.ViewHolder holder; private int fromX; private int fromY; private int toX; private int toY; public MoveInfo(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) { this.holder = holder; this.fromX = fromX; this.fromY = fromY; this.toX = toX; this.toY = toY; }}
Well, there's nothing to say, so let's continue watching it.animateMove
The method is correct.mPendingMoveHolders
Add one, but here isMoveInfo
.
@Overridepublic 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 thatint delta = toY - fromY
We calculated the distance that needs to be moved to the view to be modified, and then reversed itview.translationY
,
The purpose of this operation is to prevent the following items from moving immediately during the above process. It is offset by a certain distance.
For better understanding, we can print a set of values to help us understand.
FromY: 20, toY: 0
Delta:-20
TranslationY: 20
In this scenario, the view is first moved down by 20 pixels, and the effect is that it is not moving at the original position.
Go down and constructMoveInfo
And addmPendingMoveHolders
.
Continue ModificationrunPendingAnimations
Method, add our move operation, this part of code andadd
The code is very similar and can be copied to modify and modify.
@Overridepublic void runPendingAnimations() { boolean isRemove = !mPendingRemoveHolders.isEmpty(); boolean isMove = !mPendingMoveHolders.isEmpty(); boolean isAdd = !mPendingAddHolders.isEmpty(); if(!isRemove && !isMove && !isAdd) return; ... // then move if(isMove) { ArrayList<MoveInfo> infos = new ArrayList<>(); infos.addAll(mPendingMoveHolders); mPendingMoveHolders.clear(); for(MoveInfo info : infos) { animateMoveImpl(info); } infos.clear(); } ... // last add}
We can see that the process of moving is completely the version of add. If you don't understand it, you can flip the blog online to see the instructions in the add section.
ContinueanimateMoveImpl
Method.
// Execute the mobile animation private void animateMoveImpl (final MoveInfo info) {mMoveAnimtions. remove (info); final View view = info. holder. itemView; ObjectAnimator animator = ObjectAnimator. ofFloat (view, "translationY", view. getTranslationY (), 0); animator. setDuration (1000); animator. addListener (new AnimatorListenerAdapter () {@ Override public void onAnimationStart (Animator animation) {dispatchMoveStarting (info. hold Er) ;}@ Override public void onAnimationEnd (Animator animation) {dispatchMoveFinished (info. holder); mMoveAnimtions. remove (info. holder); if (! IsRunning () dispatchAnimationsFinished () ;}}); animator. start ();}
Here we constructtranslationY
The effect is a constant offset from the current offset of the view to 0. To put it bluntly, it is the continuous improvement. Processing at the beginning and end of an animation is the same logic as adding.
Now the code is complete, and the effect is the first effect of the blog.
Now, the only unimplemented effect ischange
In factchange
Processing andmove
The processing is also very similar,
If you are interested, referDefaultItemAnimator
Source code.
It seems that you can implementRecyclerView
The item animation is not so complicated, but the amount of code is also quite large. Is it because we need to write such a long code every time? Of course not! Observe the code carefully,
In fact, animation is implemented inanimateXXXImpl
We can fullyaninateXXXImpl
Abstract, we inherit the class to implement whatever animation is needed.aninateXXXImpl
In
The code is fine. Of course, many animation libraries are provided to us by others. We do not have to write them on our own and use them directly. The following is a great recommendation.RecyclerView
Animation library,
You can directly copy it to the project.
RecyclerView item animation library on github
The last is the demo:
Download demo, click here
Note: For attribute animation in demo, you can use it again if you need backward compatibility.nineoldandroids
OrViewCompat
.
Copyright Disclaimer: This article is an original article by the blogger and cannot be reproduced without the permission of the blogger.