Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jakubkinst/7840d707558751acae66 to your computer and use it in GitHub Desktop.
Save jakubkinst/7840d707558751acae66 to your computer and use it in GitHub Desktop.
Simply add sequential animations to any RecyclerView
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;
}
}
// 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