RecyclerView has been widely used, its various layout formats, as well as many superior features, make RecyclerView has great flexibility. One of them is the ItemAnimator, which allows you to animate various items by adding, removing, changing, and moving them. This is the main content of this article.
A, recyclerview – animators
This article will focus on recyclerview-animators, first introduce its simple use, and then introduce its realization principle.
First of all, introduced recyclerView-animators renderings. GitHub recyclerView-animators
There are two main parts to this library.
1. Customize ItemAnimator to add and delete items.
Two: Encapsulate the Adapter. In onBindViewHolder, display animation is set during binding.
rendering
Customize the ItemAnimator effect
Adapter Animation Effects
Two, recyclerview-animators use
The use of ItemAnimator
1. Introduce dependencies
compile 'jp. Wasabeef: recyclerview - animators: 2.2.6'Copy the code
2. Use
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);
recyclerView.setItemAnimator(new SlideInLeftAnimator());Copy the code
3. Advanced features
Animation duration
recyclerView.getItemAnimator(a).setAddDuration(1000);
recyclerView.getItemAnimator(a).setRemoveDuration(1000);
recyclerView.getItemAnimator(a).setMoveDuration(1000);
recyclerView.getItemAnimator(a).setChangeDuration(1000);Copy the code
interpolator
SlideInLeftAnimator animator = new SlideInLeftAnimator(a); animator.setInterpolator(new OvershootInterpolator());
recyclerView.setItemAnimator(animator);Copy the code
Also custom animation implementation
static class MyViewHolder extends RecyclerView.ViewHolder implements AnimateViewHolder {
public MyViewHolder(View itemView) {
super(itemView);
}
@Override
public void preAnimateRemoveImpl(RecyclerView.ViewHolder holder) {}@Override
public void animateRemoveImpl(RecyclerView.ViewHolder holder, ViewPropertyAnimatorListener listener) {
ViewCompat.animate(itemView)
.translationY(-itemView.getHeight() * 0.3 f)
.alpha(0)
.setDuration(300)
.setListener(listener)
.start();
}
@Override
public void preAnimateAddImpl(RecyclerView.ViewHolder holder) {
ViewCompat.setTranslationY(itemView, -itemView.getHeight() * 0.3 f);
ViewCompat.setAlpha(itemView, 0);
}
@Override
public void animateAddImpl(RecyclerView.ViewHolder holder, ViewPropertyAnimatorListener listener) {
ViewCompat.animate(itemView)
.translationY(0)
.alpha(1)
.setDuration(300) .setListener(listener) .start(); }}Copy the code
4. Pay attention to
Use the following way, will trigger the animation effect, specific analysis below:
notifyItemInserted(int)
notifyItemRemoved(int)
notifyItemRangeInserted(int, int)
notifyItemRangeRemoved(int, int)Copy the code
Such as:
public void remove(int position) {
mDataSet.remove(position);
notifyItemRemoved(position);
}
public void add(String text.int position) {
mDataSet.add(position.text);
notifyItemInserted(position);
}Copy the code
The use of the Adapter
1. Use
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);
MyAdapter adapter = new MyAdapter(a); recyclerView.setAdapter(new AlphaInAnimationAdapter(adapter));Copy the code
2. Advanced features
Animation duration
MyAdapter adapter = new MyAdapter(a);AlphaInAnimationAdapter alphaAdapter = new AlphaInAnimationAdapter(adapter);
alphaAdapter.setDuration(1000);
recyclerView.setAdapter(alphaAdapter);Copy the code
interpolator
MyAdapter adapter = new MyAdapter(a); AlphaInAnimationAdapter alphaAdapter =new AlphaInAnimationAdapter(adapter);
alphaAdapter.setInterpolator(new OvershootInterpolator());
recyclerView.setAdapter(alphaAdapter);Copy the code
Whether to show animation effects only once
MyAdapter adapter = new MyAdapter(a); AlphaInAnimationAdapter alphaAdapter =new AlphaInAnimationAdapter(adapter);
scaleAdapter.setFirstOnly(false);
recyclerView.setAdapter(alphaAdapter);Copy the code
Composite animation
MyAdapter adapter = new MyAdapter(a); AlphaInAnimationAdapter alphaAdapter =new AlphaInAnimationAdapter(adapter);
recyclerView.setAdapter(new ScaleInAnimationAdapter(alphaAdapter));Copy the code
3. The implementation principle of custom ItemAnimator
Above we analyzed the recyclerView-animators implementation, so how do we achieve their own cool animation way, and the above attention why can only use that several ways to achieve animation effect? All of this requires an understanding of how it works, so let’s focus on the implementation of custom animations.
1. Class structure
First, all custom ItemAnimators inherit from the BaseItemAnimator implementation.
public abstract class BaseItemAnimator extends SimpleItemAnimatorCopy the code
And what is the SimpleItemAnimator? Is a simple wrapper in RecyclerView according to ItemAnimator.
abstract public class SimpleItemAnimator extends RecyclerView.ItemAnimatorCopy the code
RecyclerView has the default animation effect DefaultItemAnimator, also inherited from SimpleItemAnimator, the following specific comparison and analysis.
The source of everything is the ItemAnimator, so let’s take a look at its main methods.
2.ItemAnimator
Call this method when an item in RecyclerView changes from visible to invisible on the screen
public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
@NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);Copy the code
This method is called when an item in RecyclerView is displayed on the screen
public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
@Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);Copy the code
Call notifyItemChanged(position) when the state of an item in RecyclerView changes
public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
@NonNull ViewHolder newHolder,
@NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);Copy the code
Overall planning RecyclerView all animation, unified start execution
abstract public void runPendingAnimations(a);Copy the code
These methods are key to customizing the ItemAnimator to achieve different animation effects.
SimpleItemAnimator encapsulates the above methods in order to focus on animation implementation.
3.SimpleItemAnimator
How does SimpleItemAnimator encapsulate these methods? The animateAppearance method of SimpleItemAnimator is called as follows:
public boolean animateDisappearance(@NonNull ViewHolder viewHolder,
@NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
int oldLeft = preLayoutInfo.left;
int oldTop = preLayoutInfo.top;
View disappearingItemView = viewHolder.itemView;
int newLeft = postLayoutInfo == null ? disappearingItemView.getLeft() : postLayoutInfo.left;
int newTop = postLayoutInfo == null ? disappearingItemView.getTop() : postLayoutInfo.top;
if(! viewHolder.isRemoved() && (oldLeft ! =newLeft|| oldTop ! =newTop)) {
disappearingItemView.layout(newLeft.newTop.newLeft + disappearingItemView.getWidth(),
newTop + disappearingItemView.getHeight());
if (DEBUG) {
Log.d(TAG, "DISAPPEARING: " + viewHolder + " with view " + disappearingItemView);
}
return animateMove(viewHolder, oldLeft, oldTop, newLeft.newTop);
} else {
if (DEBUG) {
Log.d(TAG, "REMOVED: " + viewHolder + " with view " + disappearingItemView);
}
returnanimateRemove(viewHolder); }}Copy the code
If the left/top coordinates of the ViewHolder are not equal, call animateMove. Otherwise, call animateAdd(ViewHolder).
The other methods are similar. In the end, we only need to implement animateRemove, animateAdd, animateMove, and animateChange.
Below we first look at the implementation of DefaultItemAnimator in RecyclerView, and then analyze BaseItemAnimator.
4.DefaultItemAnimator
The first is the implementation of animateRemove, animateAdd, animateMove, animateChange. Again, take animateAdd:
public boolean animateAdd(final ViewHolder holder) {
resetAnimation(holder);
ViewCompat.setAlpha(holder.itemView, 0);
mPendingAdditions.add(holder);
return true;
}Copy the code
First call resetAnimation(holder) to stop animating the itemView in this holder, and then set the transparency of hold. itemView to 0 (other properties can also be set, which corresponds to the initial effect of the beginning of the animation). Add holder to the mPendingAdditions collection.
Note: This collection will be iterated to perform animations in the runPendingAnimations method later.
Then there is the implementation of runPendingAnimations. Below the source code, very long, but don’t be afraid, very simple.
public void runPendingAnimations() {
booleanremovalsPending = ! mPendingRemovals.isEmpty();booleanmovesPending = ! mPendingMoves.isEmpty();booleanchangesPending = ! mPendingChanges.isEmpty();booleanadditionsPending = ! mPendingAdditions.isEmpty();if(! removalsPending && ! movesPending && ! additionsPending && ! changesPending) {// nothing to animate
return;
}
// First, remove stuff
for (ViewHolder holder : mPendingRemovals) {
animateRemoveImpl(holder);
}
mPendingRemovals.clear(a);// Next, move stuff
if (movesPending) {
final ArrayList<MoveInfo> moves = new ArrayList<>();
moves.addAll(mPendingMoves);
mMovesList.add(moves);
mPendingMoves.clear(a); Runnable mover =new Runnable() {
@Override
public void run() {
for (MoveInfo moveInfo : moves) {
animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
moveInfo.toX, moveInfo.toY);
}
moves.clear(a); mMovesList.remove(moves); }};if (removalsPending) {
View view = moves.get(0).holder.itemView;
ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
} else {
mover.run();
}
}
// Next, change stuff, to run in parallel with move animations
if (changesPending) {
final ArrayList<ChangeInfo> changes = new ArrayList<>();
changes.addAll(mPendingChanges);
mChangesList.add(changes);
mPendingChanges.clear(a); Runnable changer =new Runnable() {
@Override
public void run() {
for (ChangeInfo change : changes) {
animateChangeImpl(change);
}
changes.clear(a); mChangesList.remove(changes); }};if (removalsPending) {
ViewHolder holder = changes.get(0).oldHolder;
ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
} else {
changer.run();
}
}
// Next, add stuff
if (additionsPending) {
final ArrayList<ViewHolder> additions = new ArrayList<>();
additions.addAll(mPendingAdditions);
mAdditionsList.add(additions);
mPendingAdditions.clear(a); Runnable adder =new Runnable() {
@Override
public void run() {
for (ViewHolder holder : additions) {
animateAddImpl(holder);
}
additions.clear(a); mAdditionsList.remove(additions); }};if (removalsPending || movesPending || changesPending) {
long removeDuration = removalsPending ? getRemoveDuration() : 0;
long moveDuration = movesPending ? getMoveDuration() : 0;
long changeDuration = changesPending ? getChangeDuration() : 0;
long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
View view = additions.get(0).itemView;
ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
} else {
adder.run(a); }}}Copy the code
Remove is executed first, and the for loop iterates through all ViewHolder in the mPendingRemovals set. Once remove is complete, move and change animations begin at the same time, and finally add animations are performed.
There are several important methods to note: animateRemoveImpl(Holder); animateMoveImpl(moveInfo.holder,moveInfo.fromX, moveInfo.fromY,moveInfo.toX, moveInfo.toY); animateChangeImpl(change); animateAddImpl(holder);
These methods perform specific animation effects, see the source code.
Let’s look at the implementation of BaseItemAnimator.
5.BaseItemAnimator
Now that you know DefaultItemAnimator, the implementation of BaseItemAnimator is clear.
First, BaseItemAnimator uses the implementation of the runPendingAnimations() method in DefaultItemAnimator. Second, BaseItemAnimator uses DefaultItemAnimator in the animateMove, animateChange, and animateRemoveImpl, animateChangeImpl method implementation.
So BaseItemAnimator only left animateRemove, animateAdd method, easy to customize implementation.
Take the animateAdd method as an example:
@Override public boolean animateAdd(final ViewHolder holder) {
endAnimation(holder);
preAnimateAdd(holder);
mPendingAdditions.add(holder);
return true;
}Copy the code
Step 1: Stop animating the itemView in this holder, consistent with DefaultItemAnimator.
Step 2: Initialize the properties of itemView
Step 3: Add holder to the mPendingAdditions collection.
The main difference from DefaultItemAnimator is step 2, look at it in detail.
private void preAnimateAdd(final ViewHolder holder) {
ViewHelper.clear(holder.itemView);
if (holder instanceof AnimateViewHolder) {
((AnimateViewHolder) holder).preAnimateAddImpl(holder);
} else{ preAnimateAddImpl(holder); }}Copy the code
If the holder belongs to the AnimateViewHolder class or subclass, then its preAnimateAddImpl() method is called, otherwise the internal preAnimateAddImpl() method is called.
protected void preAnimateAddImpl(final ViewHolder holder) {}Copy the code
This method is implemented by subclasses that initialize and define various initial properties of the animation.
With regard to the AnimateViewHolder class, as mentioned earlier, this interface can be customized to implement various animation effects in place of a specific ItemAnimator, with the same internal methods as those implemented by subclasses that inherit BaseItemAnimator.
In addition, in the runPendingAnimations() method for Add, specific animations need to be specified.
Intercept the Add part of the runPendingAnimations() method.
if (additionsPending) {
final ArrayList<ViewHolder> additions = new ArrayList<ViewHolder>();
additions.addAll(mPendingAdditions);
mAdditionsList.add(additions);
mPendingAdditions.clear(a); Runnable adder =new Runnable() {
public void run() {
boolean removed = mAdditionsList.remove(additions);
if(! removed) {// already canceled
return;
}
for (ViewHolder holder : additions) {
doAnimateAdd(holder);
}
additions.clear();
}
};Copy the code
One important line is: doAnimateAdd(holder); .
private void doAnimateAdd(final ViewHolder holder) {
if (holder instanceof AnimateViewHolder) {
((AnimateViewHolder) holder).animateAddImpl(holder, new DefaultAddVpaListener(holder));
} else {
animateAddImpl(holder);
}
mAddAnimations.add(holder);
}Copy the code
As above, the animateAddImpl() method is empty and requires a subclass.
So the subclass needs to implement four methods to complete the custom animation, isn’t that easy?
6. Concrete examples
Here is an example of implementing a concrete animation:
public class FadeInAnimator extends BaseItemAnimator {
public FadeInAnimator(a) {}public FadeInAnimator(Interpolator interpolator) {
mInterpolator = interpolator;
}
@Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
ViewCompat.animate(holder.itemView)
.alpha(0)
.setDuration(getRemoveDuration())
.setInterpolator(mInterpolator)
.setListener(new DefaultRemoveVpaListener(holder))
.setStartDelay(getRemoveDelay(holder))
.start();
}
@Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) {
ViewCompat.setAlpha(holder.itemView, 0);
}
@Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) {
ViewCompat.animate(holder.itemView)
.alpha(1)
.setDuration(getAddDuration())
.setInterpolator(mInterpolator)
.setListener(newDefaultAddVpaListener(holder)) .setStartDelay(getAddDelay(holder)) .start(); }}Copy the code
It’s very simple, so we can customize the effects of adding and removing items.
4. Realization principle of Adapter animation effect
The decorator mode is used to encapsulate the original Adapter and add additional functionality.
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);
MyAdapter adapter = new MyAdapter(a); recyclerView.setAdapter(new AlphaInAnimationAdapter(adapter));Copy the code
If the original adapter is passed into a new Adpter with animated effects, how is the new Adpter implemented internally? Take AlphaInAnimationAdapter as an example:
public class AlphaInAnimationAdapter extends AnimationAdapter {
private static final float DEFAULT_ALPHA_FROM = 0f;
private final float mFrom;
public AlphaInAnimationAdapter(RecyclerView.Adapter adapter) {
this(adapter, DEFAULT_ALPHA_FROM);
}
public AlphaInAnimationAdapter(RecyclerView.Adapter adapter, float from) {
super(adapter);
mFrom = from;
}
@Override protected Animator[] getAnimators(View view) {
return new Animator[] { ObjectAnimator.ofFloat(view, "alpha", mFrom, 1f)}; }}Copy the code
As you can see, the main internal method is getAnimators, which defines the animation effects. The other methods come from the AnimationAdapter.
AnimationAdapter the onBindViewHolder is used to add animation effects
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
mAdapter.onBindViewHolder(holder, position);
int adapterPosition = holder.getAdapterPosition();
if(! isFirstOnly || adapterPosition > mLastPosition) {for (Animator anim : getAnimators(holder.itemView)) {
anim.setDuration(mDuration).start();
anim.setInterpolator(mInterpolator);
}
mLastPosition = adapterPosition;
} else {
ViewHelper.clear(holder.itemView); }}Copy the code
Loop through each item to add animation.
Five, the summary
Through the above analysis, recyclerView-Animators library has a deep understanding, based on this basis can achieve a variety of cool animation effects.
Reference article:
In-depth understanding of RecyclerView series two: ItemAnimator RecyclerView. ItemAnimator ultimate interpretation (a) – RecyclerView source code parsing RecyclerView. ItemAnimator ultimate interpretation (2) – SimpleItemAnimator and DefaultItemAnimator source code parsing RecyclerView. ItemAnimator ultimate interpretation (3) — — inherit DefaultItemAnimator implement custom animation