Advanced usage of RecyclerView-Custom Animation and recyclerview

Source: Internet
Author: User
Tags addall

Advanced usage of RecyclerView-Custom Animation and recyclerview

I believe everyone is rightRecyclerViewThe usage is quite familiar,RecyclerViewWe 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.RecyclerViewThere 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 manyRecyclerViewBut none of them are described in detail.RecyclerViewAnimation, most of which use the defaultDefaultItemAnimatorOr use a third-party animation library. This blog is used to make up for this blank space.DefaultItemAnimatorCode to implement a simpleRecyclerViewAnimation.

Let's just implement the most commonaddAndremoveIf you are interested, you can referDefaultItemAnimatorTo expand.

Before starting, let's take a look at the implementation results:

How can we customize animations? First, you must inherit fromRecyclerView.ItemAnimatorTo 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 implementaddAndremoveAnimation. ImitatingDefaultItemAnimatorIn 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 fourArrayListWhat about it? And a pair! Envy? Let's talk about it. As mentioned above, the animation may not be executed immediately, but inrunPendingAnimationsOne piece to execute, so weanimateAddOnly forwardmPendingAddHoldersAddedViewHolderInstead of writing the animation code. In this case, consider the following:

When weanimateAddOnce, thenrunPendingAnimationsThe animation is not completed yet, so we cannot clear it.mPendingAddHoldersThis set is executed again.animateAddWhat will happen? The previous animation was repeatedly executed.DefaultItemAnimatorIt 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.isRunningHow to write this method.

@Overridepublic boolean isRunning() {  return !(mPendingAddHolders.isEmpty()                    && mPendingRemoveHolders.isEmpty()                    && mAddAnimtions.isEmpty()                    && mRemoveAnimations.isEmpty());}

Not to mention, there is only one sentence:isRunningDoes it mean there is no animation to be executed.

Let's continue with the code.animateAddAndanimateRemoveMethod.

@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 thatanimateAddThe 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 judgeisRemoveTo executeremoveAnimation,
We can see that we traverse every saved ViewHolder and then executeanimateRemoveImplMethod.mPendingRemoveHoldersEmpty.
animateRemoveImplLet's leave it alone and continue looking at it.addFirstmPendingAddHoldersAdd 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.animateAddImplMethod. Finally, the local list is cleared.
Next, let's take a look.animateRemoveImplAndanimateAddImplThe 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.animateRemoveImplRight.
Add this holdermRemoveAnimationsAnd then an attribute animation that everyone is very familiar with. Here we only change the attributes of itemView In the animation.alphaValue,
As described in the document, we call this function at the beginning of an animation.dispatchRemoveStartingMethod, called at the end of the animationdispatchRemoveFinishedFinally, 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.RecyclerViewTo use our ItemAnimator.

...mRecyclerView.setAdapter(mAdapter);mRecyclerView.setItemAnimator(new MyItemAnimator());

See the results:

We can see from the results that ouraddAnimation is fine, but NimaremoveThe 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?animateMoveMethod?

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? ThisMoveInfoWhat is it? Think about it. Do you need to know the process of moving?Where is it from and where is it going?. We mimicDefaultItemAnimatorTo 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.animateMoveThe method is correct.mPendingMoveHoldersAdd 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 - fromYWe 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 constructMoveInfoAnd addmPendingMoveHolders.

Continue ModificationrunPendingAnimationsMethod, add our move operation, this part of code andaddThe 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.
ContinueanimateMoveImplMethod.

// 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 constructtranslationYThe 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 ischangeIn factchangeProcessing andmoveThe processing is also very similar,
If you are interested, referDefaultItemAnimatorSource code.
It seems that you can implementRecyclerViewThe 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 inanimateXXXImplWe can fullyaninateXXXImplAbstract, we inherit the class to implement whatever animation is needed.aninateXXXImplIn
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.RecyclerViewAnimation 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.nineoldandroidsOrViewCompat.

Copyright Disclaimer: This article is an original article by the blogger and cannot be reproduced without the permission of the blogger.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.