Last active
May 25, 2021 07:45
-
-
Save jakubkinst/7840d707558751acae66 to your computer and use it in GitHub Desktop.
Simply add sequential animations to any RecyclerView
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.view; | |
import android.animation.Animator; | |
import android.support.v7.widget.LinearLayoutManager; | |
import android.support.v7.widget.RecyclerView; | |
import android.support.v7.widget.RecyclerView.Adapter; | |
import android.support.v7.widget.RecyclerView.ViewHolder; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import android.view.animation.Interpolator; | |
import android.view.animation.LinearInterpolator; | |
public class SequentialRecyclerViewAnimationAdapter extends Adapter<ViewHolder> { | |
private static final int DEFAULT_ANIMATION_DURATION = 100; | |
private static final int DEFAULT_ANIMATION_ITEM_DELAY = 150; | |
private final RecyclerView mRecyclerView; | |
private Adapter mAdapter; | |
private int mDuration; | |
private Interpolator mInterpolator = new LinearInterpolator(); | |
private int mCounter; | |
private boolean mAnimsInitialized; | |
private int mItemDelay; | |
private AnimatorProvider mAnimatorProvider; | |
public interface AnimatorProvider { | |
Animator[] getAnimators(View v); | |
} | |
/** | |
* Creates a wrapping adapter that can be used for any RecyclerView | |
* | |
* @param adapter | |
* @param recyclerView | |
* @param animationDuration | |
* @param animationItemDelay | |
* @param animatorProvider | |
*/ | |
public SequentialRecyclerViewAnimationAdapter(Adapter<ViewHolder> adapter, RecyclerView recyclerView, int animationDuration, int animationItemDelay, AnimatorProvider animatorProvider) { | |
mAdapter = adapter; | |
mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { | |
@Override | |
public void onChanged() { | |
notifyDataSetChanged(); | |
} | |
@Override | |
public void onItemRangeChanged(int positionStart, int itemCount) { | |
notifyItemRangeChanged(positionStart, itemCount); | |
} | |
@Override | |
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { | |
notifyItemRangeChanged(positionStart, itemCount, payload); | |
} | |
@Override | |
public void onItemRangeInserted(int positionStart, int itemCount) { | |
notifyItemRangeInserted(positionStart, itemCount); | |
} | |
@Override | |
public void onItemRangeRemoved(int positionStart, int itemCount) { | |
notifyItemRangeRemoved(positionStart, itemCount); | |
} | |
@Override | |
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { | |
notifyItemMoved(fromPosition, toPosition); | |
} | |
}); | |
mRecyclerView = recyclerView; | |
mAnimatorProvider = animatorProvider; | |
mDuration = animationDuration; | |
mItemDelay = animationItemDelay; | |
} | |
/** | |
* Apply to an existing {@link RecyclerView} with an attached adapter. | |
* This will wrap and replace the original adapter | |
* | |
* @param recyclerView RecyclerView instance with an attached adapter | |
* @param animatorProvider {@link Animator} provider for the item views | |
*/ | |
public static void applyTo(RecyclerView recyclerView, AnimatorProvider animatorProvider) { | |
applyTo(recyclerView, DEFAULT_ANIMATION_DURATION, DEFAULT_ANIMATION_ITEM_DELAY, animatorProvider); | |
} | |
/** | |
* Apply to an existing {@link RecyclerView} with an attached adapter. | |
* This will wrap and replace the original adapter | |
* | |
* @param recyclerView RecyclerView instance with an attached adapter | |
* @param animationDuration Single animation duration in ms | |
* @param animationItemDelay Delay between each item's animation in ms | |
* @param animatorProvider {@link Animator} provider for the item views | |
*/ | |
public static void applyTo(RecyclerView recyclerView, int animationDuration, int animationItemDelay, AnimatorProvider animatorProvider) { | |
if(recyclerView.getAdapter() == null) | |
throw new IllegalStateException("RecyclerView must have an adapter attached"); | |
SequentialRecyclerViewAnimationAdapter adapter = new SequentialRecyclerViewAnimationAdapter(recyclerView.getAdapter(), recyclerView, animationDuration, animationItemDelay, animatorProvider); | |
recyclerView.setAdapter(adapter); | |
} | |
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | |
return mAdapter.onCreateViewHolder(parent, viewType); | |
} | |
public void onBindViewHolder(ViewHolder holder, int position) { | |
mAdapter.onBindViewHolder(holder, position); | |
Animator[] animators = getAnimators(holder.itemView); | |
LinearLayoutManager layoutManager = ((LinearLayoutManager) getRecyclerView().getLayoutManager()); | |
int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition(); | |
if(!mAnimsInitialized && (firstVisiblePosition == 0 || position == 0)) | |
for(Animator anim : animators) { | |
anim.setDuration((long) mDuration); | |
anim.setInterpolator(mInterpolator); | |
increaseCounter(); | |
anim.setStartDelay(getItemDelay() * position); | |
anim.addListener(new Animator.AnimatorListener() { | |
@Override | |
public void onAnimationStart(Animator animation) { | |
} | |
@Override | |
public void onAnimationEnd(Animator animation) { | |
if(decreaseCounter()) | |
mAnimsInitialized = true; | |
} | |
@Override | |
public void onAnimationCancel(Animator animation) { | |
} | |
@Override | |
public void onAnimationRepeat(Animator animation) { | |
} | |
}); | |
anim.start(); | |
} | |
else | |
for(Animator animator : animators) { | |
animator.setDuration(0); | |
animator.start(); | |
} | |
} | |
public int getItemDelay() { | |
return mItemDelay; | |
} | |
public void setAdapter(Adapter adapter) { | |
mAdapter = adapter; | |
} | |
public AnimatorProvider getAnimatorProvider() { | |
return mAnimatorProvider; | |
} | |
protected synchronized boolean decreaseCounter() { | |
mCounter--; | |
return mCounter == 0; | |
} | |
public synchronized int getCounter() { | |
return mCounter; | |
} | |
public synchronized void increaseCounter() { | |
mCounter++; | |
} | |
public int getItemCount() { | |
return mAdapter.getItemCount(); | |
} | |
public void setDuration(int duration) { | |
mDuration = duration; | |
} | |
public void setItemDelay(int itemDelay) { | |
mItemDelay = itemDelay; | |
} | |
public void setInterpolator(Interpolator interpolator) { | |
mInterpolator = interpolator; | |
} | |
private Animator[] getAnimators(View view) { | |
return getAnimatorProvider().getAnimators(view); | |
} | |
public RecyclerView getRecyclerView() { | |
return mRecyclerView; | |
} | |
public int getItemViewType(int position) { | |
return mAdapter.getItemViewType(position); | |
} | |
public Adapter<ViewHolder> getWrappedAdapter() { | |
return mAdapter; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Alpha animation (0->1) | |
// When the recyclerView has an adapter attached | |
SequentialRecyclerViewAnimationAdapter.applyTo(getBinding().eventList, view -> { | |
view.setAlpha(0); | |
return new Animator[]{ObjectAnimator.ofFloat(view, "alpha", 0.0F, 1.0F)}; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment