package com.mercandalli.android.apps.files.common.animation;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
/**
* {@link RecyclerView} adapter with animation.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class ScaleAnimationAdapter extends RecyclerView.Adapter<ViewHolder> implements
Animator.AnimatorListener {
/**
* The {@link RecyclerView.Adapter} with animated {@link ViewHolder}.
*/
@NonNull
private final RecyclerView.Adapter mAdapter;
/**
* The {@link RecyclerView} with animated {@link ViewHolder}.
*/
@NonNull
private final RecyclerView mRecyclerView;
/**
* The animation duration for each {@link RecyclerView}.
*/
private int mDuration = 300;
/**
* The animation offset duration between each {@link RecyclerView}.
*/
private int mOffsetDuration = 100;
/**
* The animation {@link Interpolator}.
*/
@NonNull
private Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
/**
* The number of {@link ViewHolder} items per line.
*/
private int mViewHolderPerLine;
private int mLastPosition = -1;
private boolean mIsFirstOnly = false;
private int mCounter;
private boolean mAnimsInitialized;
private final float mFrom;
@Nullable
private NoAnimatedPosition mNoAnimatedPosition;
public ScaleAnimationAdapter(RecyclerView recyclerView, RecyclerView.Adapter adapter) {
this(recyclerView, adapter, 1, 0.0F);
}
public ScaleAnimationAdapter(RecyclerView recyclerView, RecyclerView.Adapter adapter, int cardPerLine) {
this(recyclerView, adapter, cardPerLine, 0.0F);
}
public ScaleAnimationAdapter(
@NonNull final RecyclerView recyclerView,
@NonNull final RecyclerView.Adapter adapter,
int cardPerLine,
float from) {
setHasStableIds(true);
mRecyclerView = recyclerView;
mAdapter = adapter;
mViewHolderPerLine = cardPerLine;
mFrom = from;
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
mLastPosition = -1;
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) {
notifyDataSetChanged();
}
});
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return this.mAdapter.onCreateViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
this.mAdapter.onBindViewHolder(holder, position);
if (this.mIsFirstOnly && position <= this.mLastPosition) {
clear(holder.itemView);
} else if (mNoAnimatedPosition == null || mNoAnimatedPosition.isAnimatedItem(position)) {
final Animator[] animators = this.getAnimators(holder.itemView);
if (!mAnimsInitialized) {
for (Animator anim : animators) {
anim.setDuration((long) this.mDuration);
anim.setInterpolator(this.mInterpolator);
increaseCounter();
if (mViewHolderPerLine > 1 && position >= mViewHolderPerLine) {
anim.setStartDelay(mOffsetDuration * (position - mViewHolderPerLine / 2));
} else {
anim.setStartDelay(mOffsetDuration * position);
}
anim.addListener(this);
anim.start();
}
} else {
holder.itemView.setScaleX(1);
holder.itemView.setScaleY(1);
}
this.mLastPosition = position;
} else {
holder.itemView.setScaleX(1);
holder.itemView.setScaleY(1);
this.mLastPosition = position;
}
}
@Override
public long getItemId(int position) {
return mAdapter.getItemId(position);
}
@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) {
}
public synchronized int getCounter() {
return mCounter;
}
public synchronized void increaseCounter() {
mCounter++;
}
public int getItemCount() {
return this.mAdapter.getItemCount();
}
public void setDuration(int duration) {
this.mDuration = duration;
}
public void setOffsetDuration(int mOffsetDuration) {
this.mOffsetDuration = mOffsetDuration;
}
public void setInterpolator(@NonNull final Interpolator interpolator) {
this.mInterpolator = interpolator;
}
public void setStartPosition(int start) {
this.mLastPosition = start;
}
private RecyclerView getRecyclerView() {
return mRecyclerView;
}
public int getItemViewType(int position) {
return this.mAdapter.getItemViewType(position);
}
private synchronized boolean decreaseCounter() {
mCounter--;
return mCounter == 0;
}
private Animator[] getAnimators(View view) {
view.setScaleX(0);
view.setScaleY(0);
return new Animator[]{ObjectAnimator.ofFloat(view, "scaleX", this.mFrom, 1.0F), ObjectAnimator.ofFloat(view, "scaleY", this.mFrom, 1.0F)};
}
private static void clear(@NonNull final View v) {
ViewCompat.setAlpha(v, 1.0F);
ViewCompat.setScaleY(v, 1.0F);
ViewCompat.setScaleX(v, 1.0F);
v.setPivotY((float) (v.getMeasuredHeight() / 2));
ViewCompat.setPivotX(v, (float) (v.getMeasuredWidth() / 2));
ViewCompat.animate(v).setInterpolator(null);
}
public void setNoAnimatedPosition(@Nullable final NoAnimatedPosition noAnimatedPosition) {
mNoAnimatedPosition = noAnimatedPosition;
}
public void reset() {
mLastPosition = -1;
mAnimsInitialized = false;
}
public interface NoAnimatedPosition {
/**
* The the item not animated.
*
* @param position The position to check.
* @return True if the item is animated.
*/
boolean isAnimatedItem(int position);
}
}