Skip to content

Instantly share code, notes, and snippets.

@gabrielemariotti
Last active June 14, 2018 08:40
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save gabrielemariotti/de7a97dffea78588b335 to your computer and use it in GitHub Desktop.
Save gabrielemariotti/de7a97dffea78588b335 to your computer and use it in GitHub Desktop.
SlideInOutLeftDefaultItemAnimator : a simple animator which applies a slide in/out from/to the left animation to item views in the `RecyclerView`

This class provides a simple animator which applies a slide in/out from/to the left animation to item views in the RecyclerView

This code is cloned from DefaultItemAnimator provided by Google, customizing the part highlighted as "Custom implementation".

Example:

   mRecyclerView.setItemAnimator(new SlideInOutLeftDefaultItemAnimator(mRecyclerView));
public class SlideInOutLeftDefaultItemAnimator extends RecyclerView.ItemAnimator {
//---------------------------------------------------------------------
//Custom implementation
//---------------------------------------------------------------------
RecyclerView mRecyclerView;
SlideInOutLeftDefaultItemAnimator(RecyclerView recyclerView){
mRecyclerView = recyclerView;
}
private void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
final View view = holder.itemView;
ViewCompat.animate(view).cancel();
ViewCompat.animate(view).setDuration(getRemoveDuration()).
translationX(-mRecyclerView.getWidth()).setListener(new VpaListenerAdapter() {
@Override
public void onAnimationEnd(View view) {
ViewCompat.setTranslationX(view, -mRecyclerView.getWidth());
dispatchRemoveFinished(holder);
mRemoveAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
mRemoveAnimations.add(holder);
}
@Override
public boolean animateAdd(final RecyclerView.ViewHolder holder) {
ViewCompat.setTranslationX(holder.itemView, -mRecyclerView.getWidth());
mPendingAdditions.add(holder);
return true;
}
private void animateAddImpl(final RecyclerView.ViewHolder holder) {
final View view = holder.itemView;
ViewCompat.animate(view).cancel();
ViewCompat.animate(view).translationX(0)
.setDuration(getAddDuration()).
setListener(new VpaListenerAdapter() {
@Override
public void onAnimationCancel(View view) {
ViewCompat.setTranslationX(view, 0);
}
@Override
public void onAnimationEnd(View view) {
dispatchAddFinished(holder);
mAddAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
mAddAnimations.add(holder);
}
//---------------------------------------------------------------------
// DefaultItemAnimator provided by Google in rc1
//---------------------------------------------------------------------
private ArrayList<RecyclerView.ViewHolder> mPendingRemovals = new ArrayList<RecyclerView.ViewHolder>();
private ArrayList<RecyclerView.ViewHolder> mPendingAdditions = new ArrayList<RecyclerView.ViewHolder>();
private ArrayList<MoveInfo> mPendingMoves = new ArrayList<MoveInfo>();
private ArrayList<RecyclerView.ViewHolder> mAdditions = new ArrayList<RecyclerView.ViewHolder>();
private ArrayList<MoveInfo> mMoves = new ArrayList<MoveInfo>();
private ArrayList<RecyclerView.ViewHolder> mAddAnimations = new ArrayList<RecyclerView.ViewHolder>();
private ArrayList<RecyclerView.ViewHolder> mMoveAnimations = new ArrayList<RecyclerView.ViewHolder>();
private ArrayList<RecyclerView.ViewHolder> mRemoveAnimations = new ArrayList<RecyclerView.ViewHolder>();
private static class MoveInfo {
public RecyclerView.ViewHolder holder;
public int fromX, fromY, toX, toY;
private 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;
}
}
@Override
public void runPendingAnimations() {
boolean removalsPending = !mPendingRemovals.isEmpty();
boolean movesPending = !mPendingMoves.isEmpty();
boolean additionsPending = !mPendingAdditions.isEmpty();
if (!removalsPending && !movesPending && !additionsPending) {
// nothing to animate
return;
}
// First, remove stuff
for (RecyclerView.ViewHolder holder : mPendingRemovals) {
animateRemoveImpl(holder);
}
mPendingRemovals.clear();
// Next, move stuff
if (movesPending) {
mMoves.addAll(mPendingMoves);
mPendingMoves.clear();
Runnable mover = new Runnable() {
@Override
public void run() {
for (MoveInfo moveInfo : mMoves) {
animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
moveInfo.toX, moveInfo.toY);
}
mMoves.clear();
}
};
if (removalsPending) {
View view = mMoves.get(0).holder.itemView;
ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
} else {
mover.run();
}
}
// Next, add stuff
if (additionsPending) {
mAdditions.addAll(mPendingAdditions);
mPendingAdditions.clear();
Runnable adder = new Runnable() {
public void run() {
for (RecyclerView.ViewHolder holder : mAdditions) {
animateAddImpl(holder);
}
mAdditions.clear();
}
};
if (removalsPending || movesPending) {
View view = mAdditions.get(0).itemView;
ViewCompat.postOnAnimationDelayed(view, adder,
(removalsPending ? getRemoveDuration() : 0) +
(movesPending ? getMoveDuration() : 0));
} else {
adder.run();
}
}
}
@Override
public boolean animateRemove(final RecyclerView.ViewHolder holder) {
mPendingRemovals.add(holder);
return true;
}
@Override
public boolean animateMove(final RecyclerView.ViewHolder holder, int fromX, int fromY,
int toX, int toY) {
final View view = holder.itemView;
int deltaX = toX - fromX;
int deltaY = toY - fromY;
if (deltaX == 0 && deltaY == 0) {
dispatchMoveFinished(holder);
return false;
}
if (deltaX != 0) {
ViewCompat.setTranslationX(view, -deltaX);
}
if (deltaY != 0) {
ViewCompat.setTranslationY(view, -deltaY);
}
mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
return true;
}
private void animateMoveImpl(final RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
final View view = holder.itemView;
final int deltaX = toX - fromX;
final int deltaY = toY - fromY;
ViewCompat.animate(view).cancel();
if (deltaX != 0) {
ViewCompat.animate(view).translationX(0);
}
if (deltaY != 0) {
ViewCompat.animate(view).translationY(0);
}
// TODO: make EndActions end listeners instead, since end actions aren't called when
// vpas are canceled (and can't end them. why?)
// need listener functionality in VPACompat for this. Ick.
ViewCompat.animate(view).setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {
@Override
public void onAnimationCancel(View view) {
if (deltaX != 0) {
ViewCompat.setTranslationX(view, 0);
}
if (deltaY != 0) {
ViewCompat.setTranslationY(view, 0);
}
}
@Override
public void onAnimationEnd(View view) {
dispatchMoveFinished(holder);
mMoveAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
mMoveAnimations.add(holder);
}
@Override
public void endAnimation(RecyclerView.ViewHolder item) {
final View view = item.itemView;
ViewCompat.animate(view).cancel();
if (mPendingMoves.contains(item)) {
ViewCompat.setTranslationY(view, 0);
ViewCompat.setTranslationX(view, 0);
dispatchMoveFinished(item);
mPendingMoves.remove(item);
}
if (mPendingRemovals.contains(item)) {
dispatchRemoveFinished(item);
mPendingRemovals.remove(item);
}
if (mPendingAdditions.contains(item)) {
ViewCompat.setAlpha(view, 1);
dispatchAddFinished(item);
mPendingAdditions.remove(item);
}
if (mMoveAnimations.contains(item)) {
ViewCompat.setTranslationY(view, 0);
ViewCompat.setTranslationX(view, 0);
dispatchMoveFinished(item);
mMoveAnimations.remove(item);
}
if (mRemoveAnimations.contains(item)) {
ViewCompat.setAlpha(view, 1);
dispatchRemoveFinished(item);
mRemoveAnimations.remove(item);
}
if (mAddAnimations.contains(item)) {
ViewCompat.setAlpha(view, 1);
dispatchAddFinished(item);
mAddAnimations.remove(item);
}
dispatchFinishedWhenDone();
}
@Override
public boolean isRunning() {
return (!mMoveAnimations.isEmpty() ||
!mRemoveAnimations.isEmpty() ||
!mAddAnimations.isEmpty() ||
!mMoves.isEmpty() ||
!mAdditions.isEmpty());
}
/**
* Check the state of currently pending and running animations. If there are none
* pending/running, call {@link #dispatchAnimationsFinished()} to notify any
* listeners.
*/
private void dispatchFinishedWhenDone() {
if (!isRunning()) {
dispatchAnimationsFinished();
}
}
@Override
public void endAnimations() {
int count = mPendingMoves.size();
for (int i = count - 1; i >= 0; i--) {
MoveInfo item = mPendingMoves.get(i);
View view = item.holder.itemView;
ViewCompat.animate(view).cancel();
ViewCompat.setTranslationY(view, 0);
ViewCompat.setTranslationX(view, 0);
dispatchMoveFinished(item.holder);
mPendingMoves.remove(item);
}
count = mPendingRemovals.size();
for (int i = count - 1; i >= 0; i--) {
RecyclerView.ViewHolder item = mPendingRemovals.get(i);
dispatchRemoveFinished(item);
mPendingRemovals.remove(item);
}
count = mPendingAdditions.size();
for (int i = count - 1; i >= 0; i--) {
RecyclerView.ViewHolder item = mPendingAdditions.get(i);
View view = item.itemView;
ViewCompat.setAlpha(view, 1);
dispatchAddFinished(item);
mPendingAdditions.remove(item);
}
if (!isRunning()) {
return;
}
count = mMoveAnimations.size();
for (int i = count - 1; i >= 0; i--) {
RecyclerView.ViewHolder item = mMoveAnimations.get(i);
View view = item.itemView;
ViewCompat.animate(view).cancel();
ViewCompat.setTranslationY(view, 0);
ViewCompat.setTranslationX(view, 0);
dispatchMoveFinished(item);
mMoveAnimations.remove(item);
}
count = mRemoveAnimations.size();
for (int i = count - 1; i >= 0; i--) {
RecyclerView.ViewHolder item = mRemoveAnimations.get(i);
View view = item.itemView;
ViewCompat.animate(view).cancel();
ViewCompat.setAlpha(view, 1);
dispatchRemoveFinished(item);
mRemoveAnimations.remove(item);
}
count = mAddAnimations.size();
for (int i = count - 1; i >= 0; i--) {
RecyclerView.ViewHolder item = mAddAnimations.get(i);
View view = item.itemView;
ViewCompat.animate(view).cancel();
ViewCompat.setAlpha(view, 1);
dispatchAddFinished(item);
mAddAnimations.remove(item);
}
mMoves.clear();
mAdditions.clear();
dispatchAnimationsFinished();
}
private static class VpaListenerAdapter implements ViewPropertyAnimatorListener {
@Override
public void onAnimationStart(View view) {}
@Override
public void onAnimationEnd(View view) {}
@Override
public void onAnimationCancel(View view) {}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment