Loading app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItemAnimator.kt +16 −4 Original line number Diff line number Diff line Loading @@ -9,9 +9,21 @@ class MessageListItemAnimator : DefaultItemAnimator() { changeDuration = 120 } override fun canReuseUpdatedViewHolder(viewHolder: ViewHolder, payloads: MutableList<Any>): Boolean { // ItemTouchHelper expects swiped views to be removed from the view hierarchy. So we don't reuse views that are // marked as having been swiped. return !viewHolder.wasSwiped && super.canReuseUpdatedViewHolder(viewHolder, payloads) override fun animateChange( oldHolder: ViewHolder, newHolder: ViewHolder, fromX: Int, fromY: Int, toX: Int, toY: Int ): Boolean { if (oldHolder == newHolder && newHolder.wasSwiped) { // Don't touch views currently being swiped dispatchChangeFinished(oldHolder, true) dispatchChangeFinished(newHolder, false) return false } return super.animateChange(oldHolder, newHolder, fromX, fromY, toX, toY) } } app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListSwipeCallback.kt +7 −9 Original line number Diff line number Diff line Loading @@ -72,8 +72,7 @@ class MessageListSwipeCallback( val holder = viewHolder as MessageViewHolder val item = adapter.getItemById(holder.uniqueId) ?: error("Couldn't find MessageListItem") // ItemTouchHelper expects swiped views to be removed from the view hierarchy. We mark this ViewHolder so that // MessageListItemAnimator knows not to reuse it during an animation. // Mark view to prevent MessageListItemAnimator from interfering with swipe animations viewHolder.markAsSwiped(true) when (direction) { Loading @@ -99,26 +98,25 @@ class MessageListSwipeCallback( dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean isCurrentlyActive: Boolean, success: Boolean ) { val view = viewHolder.itemView val viewWidth = view.width val viewHeight = view.height val isViewAnimatingBack = !isCurrentlyActive if (dX != 0F) { canvas.withTranslation(x = view.left.toFloat(), y = view.top.toFloat()) { if (isViewAnimatingBack) { drawBackground(dX, viewWidth, viewHeight) } else { if (isCurrentlyActive || !success) { val holder = viewHolder as MessageViewHolder drawLayout(dX, viewWidth, viewHeight, holder) } else { drawBackground(dX, viewWidth, viewHeight) } } } super.onChildDraw(canvas, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) super.onChildDraw(canvas, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive, success) } private fun Canvas.drawBackground(dX: Float, width: Int, height: Int) { Loading ui-utils/ItemTouchHelper/src/main/java/app/k9mail/ui/utils/itemtouchhelper/ItemTouchHelper.java +127 −57 Original line number Diff line number Diff line Loading @@ -615,75 +615,68 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration final float currentTranslateX = limitDeltaX(mRecyclerView, mTmpPosition[0]); final float currentTranslateY = mTmpPosition[1]; // find where we should animate to final float targetTranslateX, targetTranslateY; int animationType; final int animationType; if (prevActionState == ACTION_STATE_DRAG) { animationType = ANIMATION_TYPE_DRAG; } else if (swipeDir > 0) { animationType = ANIMATION_TYPE_SWIPE_SUCCESS; } else { animationType = ANIMATION_TYPE_SWIPE_CANCEL; } final RecoverAnimation animation; final boolean useDefaultDuration; switch (swipeDir) { case LEFT: case RIGHT: case START: case END: if (mCallback.shouldAnimateOut(swipeDir)) { targetTranslateX = Math.signum(mDx) * mRecyclerView.getWidth(); float targetTranslateX = Math.signum(mDx) * mRecyclerView.getWidth(); animation = new MoveOutAnimation(prevSelected, animationType, prevActionState, currentTranslateX, currentTranslateY, targetTranslateX, currentTranslateY, swipeDir, /* moveBackAfterwards */ false); useDefaultDuration = true; } else if (wasFling) { int maxSwipeDistance = mCallback.getMaxSwipeDistance(mRecyclerView, swipeDir); targetTranslateX = Math.signum(mDx) * maxSwipeDistance; float targetTranslateX = Math.signum(mDx) * maxSwipeDistance; animation = new MoveOutAnimation(prevSelected, animationType, prevActionState, currentTranslateX, currentTranslateY, targetTranslateX, currentTranslateY, swipeDir, /* moveBackAfterwards */ true); useDefaultDuration = true; } else { targetTranslateX = currentTranslateX; // This is a dummy animation to ensure mCallback.onChildDraw() calls will be made even if // the animating back part is delayed. animation = new MoveOutAnimation(prevSelected, animationType, prevActionState, currentTranslateX, currentTranslateY, currentTranslateX, currentTranslateY, swipeDir, /* moveBackAfterwards */ true); animation.setDuration(0); useDefaultDuration = false; } targetTranslateY = 0; break; case UP: case DOWN: targetTranslateX = 0; targetTranslateY = Math.signum(mDy) * mRecyclerView.getHeight(); break; throw new UnsupportedOperationException(); default: targetTranslateX = 0; targetTranslateY = 0; } if (prevActionState == ACTION_STATE_DRAG) { animationType = ANIMATION_TYPE_DRAG; } else if (swipeDir > 0) { animationType = ANIMATION_TYPE_SWIPE_SUCCESS; } else { animationType = ANIMATION_TYPE_SWIPE_CANCEL; animation = new MoveBackAnimation(prevSelected, animationType, prevActionState, currentTranslateX, currentTranslateY); useDefaultDuration = true; } final RecoverAnimation rv = new RecoverAnimation(prevSelected, animationType, prevActionState, currentTranslateX, currentTranslateY, targetTranslateX, targetTranslateY) { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (this.mOverridden) { return; } if (swipeDir <= 0) { // this is a drag or failed swipe. recover immediately mCallback.clearView(mRecyclerView, prevSelected); // full cleanup will happen on onDrawOver } else { // wait until remove animation is complete. mPendingCleanup.add(prevSelected.itemView); mIsPendingCleanup = true; if (swipeDir > 0) { // Animation might be ended by other animators during a layout. // We defer callback to avoid editing adapter during a layout. postDispatchSwipe(this, swipeDir); if (useDefaultDuration) { long duration = mCallback.getAnimationDuration(mRecyclerView, animationType, animation.mTargetX - animation.mStartDx, animation.mTargetY - animation.mStartDy); animation.setDuration(duration); } } // removed from the list after it is drawn for the last time if (mOverdrawChild == prevSelected.itemView) { removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView); } } }; final long duration = mCallback.getAnimationDuration(mRecyclerView, animationType, targetTranslateX - currentTranslateX, targetTranslateY - currentTranslateY); rv.setDuration(duration); mRecoverAnimations.add(rv); rv.start(); mRecoverAnimations.add(animation); animation.start(); preventLayout = true; } else { removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView); Loading Loading @@ -715,7 +708,7 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration } @SuppressWarnings("WeakerAccess") /* synthetic access */ void postDispatchSwipe(final RecoverAnimation anim, final int swipeDir) { void postDispatchSwipe(final RecoverAnimation anim, final int swipeDir, final boolean moveBackAfterwards) { // wait until animations are complete. mRecyclerView.post(new Runnable() { @Override Loading @@ -731,6 +724,9 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration if ((animator == null || !animator.isRunning(null)) && !hasRunningRecoverAnim()) { mCallback.onSwiped(anim.mViewHolder, swipeDir); if (moveBackAfterwards) { startMoveBackAnimation(anim); } } else { mRecyclerView.post(this); } Loading @@ -739,6 +735,20 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration }); } private void startMoveBackAnimation(RecoverAnimation animation) { MoveBackAnimation moveBackAnimation = new MoveBackAnimation(animation.mViewHolder, animation.mAnimationType, animation.mActionState, animation.mTargetX, animation.mTargetY); long duration = mCallback.getAnimationDuration(mRecyclerView, animation.mAnimationType, -animation.mTargetX, -animation.mTargetY); moveBackAnimation.setDuration(duration); mRecoverAnimations.remove(animation); mRecoverAnimations.add(moveBackAnimation); moveBackAnimation.start(); } @SuppressWarnings("WeakerAccess") /* synthetic access */ boolean hasRunningRecoverAnim() { final int size = mRecoverAnimations.size(); Loading Loading @@ -2005,13 +2015,15 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration final ItemTouchHelper.RecoverAnimation anim = recoverAnimationList.get(i); anim.update(); final int count = c.save(); boolean isCurrentlyActive = anim instanceof MoveOutAnimation; boolean success = anim.mAnimationType == ANIMATION_TYPE_SWIPE_SUCCESS; onChildDraw(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState, anim.mAnimationType == ANIMATION_TYPE_SWIPE_SUCCESS && !anim.mIsPendingCleanup); isCurrentlyActive, success); c.restoreToCount(count); } if (selected != null) { final int count = c.save(); onChildDraw(c, parent, selected, dX, dY, actionState, true); onChildDraw(c, parent, selected, dX, dY, actionState, true, false); c.restoreToCount(count); } } Loading Loading @@ -2053,7 +2065,7 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration * This is a good place to clear all changes on the View that was done in * {@link #onSelectedChanged(RecyclerView.ViewHolder, int)}, * {@link #onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int, * boolean)} or * boolean, boolean)} or * {@link #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int, boolean)}. * * @param recyclerView The RecyclerView which is controlled by the ItemTouchHelper. Loading Loading @@ -2092,7 +2104,7 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration */ public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { float dX, float dY, int actionState, boolean isCurrentlyActive, boolean success) { ItemTouchUIUtilImpl.INSTANCE.onDraw(c, recyclerView, viewHolder.itemView, dX, dY, actionState, isCurrentlyActive); } Loading Loading @@ -2526,4 +2538,62 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration } } private class MoveBackAnimation extends RecoverAnimation { MoveBackAnimation(ViewHolder viewHolder, int animationType, int actionState, float startDx, float startDy) { super(viewHolder, animationType, actionState, startDx, startDy, /* targetX */ 0, /* targetY */ 0); } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (this.mOverridden) { return; } mCallback.clearView(mRecyclerView, mViewHolder); // full cleanup will happen on onDrawOver // removed from the list after it is drawn for the last time if (mOverdrawChild == mViewHolder.itemView) { removeChildDrawingOrderCallbackIfNecessary(mViewHolder.itemView); } } } private class MoveOutAnimation extends RecoverAnimation { private final int mSwipeDirection; private final boolean mMoveBackAfterwards; MoveOutAnimation(ViewHolder viewHolder, int animationType, int actionState, float startDx, float startDy, float targetX, float targetY, int swipeDirection, boolean moveBackAfterwards) { super(viewHolder, animationType, actionState, startDx, startDy, targetX, targetY); this.mSwipeDirection = swipeDirection; this.mMoveBackAfterwards = moveBackAfterwards; } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (this.mOverridden) { return; } if (!mMoveBackAfterwards) { mPendingCleanup.add(mViewHolder.itemView); } mIsPendingCleanup = true; // Animation might be ended by other animators during a layout. // We defer callback to avoid editing adapter during a layout. postDispatchSwipe(this, mSwipeDirection, mMoveBackAfterwards); if (!mMoveBackAfterwards) { // removed from the list after it is drawn for the last time if (mOverdrawChild == mViewHolder.itemView) { removeChildDrawingOrderCallbackIfNecessary(mViewHolder.itemView); } } } } } Loading
app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItemAnimator.kt +16 −4 Original line number Diff line number Diff line Loading @@ -9,9 +9,21 @@ class MessageListItemAnimator : DefaultItemAnimator() { changeDuration = 120 } override fun canReuseUpdatedViewHolder(viewHolder: ViewHolder, payloads: MutableList<Any>): Boolean { // ItemTouchHelper expects swiped views to be removed from the view hierarchy. So we don't reuse views that are // marked as having been swiped. return !viewHolder.wasSwiped && super.canReuseUpdatedViewHolder(viewHolder, payloads) override fun animateChange( oldHolder: ViewHolder, newHolder: ViewHolder, fromX: Int, fromY: Int, toX: Int, toY: Int ): Boolean { if (oldHolder == newHolder && newHolder.wasSwiped) { // Don't touch views currently being swiped dispatchChangeFinished(oldHolder, true) dispatchChangeFinished(newHolder, false) return false } return super.animateChange(oldHolder, newHolder, fromX, fromY, toX, toY) } }
app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListSwipeCallback.kt +7 −9 Original line number Diff line number Diff line Loading @@ -72,8 +72,7 @@ class MessageListSwipeCallback( val holder = viewHolder as MessageViewHolder val item = adapter.getItemById(holder.uniqueId) ?: error("Couldn't find MessageListItem") // ItemTouchHelper expects swiped views to be removed from the view hierarchy. We mark this ViewHolder so that // MessageListItemAnimator knows not to reuse it during an animation. // Mark view to prevent MessageListItemAnimator from interfering with swipe animations viewHolder.markAsSwiped(true) when (direction) { Loading @@ -99,26 +98,25 @@ class MessageListSwipeCallback( dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean isCurrentlyActive: Boolean, success: Boolean ) { val view = viewHolder.itemView val viewWidth = view.width val viewHeight = view.height val isViewAnimatingBack = !isCurrentlyActive if (dX != 0F) { canvas.withTranslation(x = view.left.toFloat(), y = view.top.toFloat()) { if (isViewAnimatingBack) { drawBackground(dX, viewWidth, viewHeight) } else { if (isCurrentlyActive || !success) { val holder = viewHolder as MessageViewHolder drawLayout(dX, viewWidth, viewHeight, holder) } else { drawBackground(dX, viewWidth, viewHeight) } } } super.onChildDraw(canvas, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) super.onChildDraw(canvas, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive, success) } private fun Canvas.drawBackground(dX: Float, width: Int, height: Int) { Loading
ui-utils/ItemTouchHelper/src/main/java/app/k9mail/ui/utils/itemtouchhelper/ItemTouchHelper.java +127 −57 Original line number Diff line number Diff line Loading @@ -615,75 +615,68 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration final float currentTranslateX = limitDeltaX(mRecyclerView, mTmpPosition[0]); final float currentTranslateY = mTmpPosition[1]; // find where we should animate to final float targetTranslateX, targetTranslateY; int animationType; final int animationType; if (prevActionState == ACTION_STATE_DRAG) { animationType = ANIMATION_TYPE_DRAG; } else if (swipeDir > 0) { animationType = ANIMATION_TYPE_SWIPE_SUCCESS; } else { animationType = ANIMATION_TYPE_SWIPE_CANCEL; } final RecoverAnimation animation; final boolean useDefaultDuration; switch (swipeDir) { case LEFT: case RIGHT: case START: case END: if (mCallback.shouldAnimateOut(swipeDir)) { targetTranslateX = Math.signum(mDx) * mRecyclerView.getWidth(); float targetTranslateX = Math.signum(mDx) * mRecyclerView.getWidth(); animation = new MoveOutAnimation(prevSelected, animationType, prevActionState, currentTranslateX, currentTranslateY, targetTranslateX, currentTranslateY, swipeDir, /* moveBackAfterwards */ false); useDefaultDuration = true; } else if (wasFling) { int maxSwipeDistance = mCallback.getMaxSwipeDistance(mRecyclerView, swipeDir); targetTranslateX = Math.signum(mDx) * maxSwipeDistance; float targetTranslateX = Math.signum(mDx) * maxSwipeDistance; animation = new MoveOutAnimation(prevSelected, animationType, prevActionState, currentTranslateX, currentTranslateY, targetTranslateX, currentTranslateY, swipeDir, /* moveBackAfterwards */ true); useDefaultDuration = true; } else { targetTranslateX = currentTranslateX; // This is a dummy animation to ensure mCallback.onChildDraw() calls will be made even if // the animating back part is delayed. animation = new MoveOutAnimation(prevSelected, animationType, prevActionState, currentTranslateX, currentTranslateY, currentTranslateX, currentTranslateY, swipeDir, /* moveBackAfterwards */ true); animation.setDuration(0); useDefaultDuration = false; } targetTranslateY = 0; break; case UP: case DOWN: targetTranslateX = 0; targetTranslateY = Math.signum(mDy) * mRecyclerView.getHeight(); break; throw new UnsupportedOperationException(); default: targetTranslateX = 0; targetTranslateY = 0; } if (prevActionState == ACTION_STATE_DRAG) { animationType = ANIMATION_TYPE_DRAG; } else if (swipeDir > 0) { animationType = ANIMATION_TYPE_SWIPE_SUCCESS; } else { animationType = ANIMATION_TYPE_SWIPE_CANCEL; animation = new MoveBackAnimation(prevSelected, animationType, prevActionState, currentTranslateX, currentTranslateY); useDefaultDuration = true; } final RecoverAnimation rv = new RecoverAnimation(prevSelected, animationType, prevActionState, currentTranslateX, currentTranslateY, targetTranslateX, targetTranslateY) { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (this.mOverridden) { return; } if (swipeDir <= 0) { // this is a drag or failed swipe. recover immediately mCallback.clearView(mRecyclerView, prevSelected); // full cleanup will happen on onDrawOver } else { // wait until remove animation is complete. mPendingCleanup.add(prevSelected.itemView); mIsPendingCleanup = true; if (swipeDir > 0) { // Animation might be ended by other animators during a layout. // We defer callback to avoid editing adapter during a layout. postDispatchSwipe(this, swipeDir); if (useDefaultDuration) { long duration = mCallback.getAnimationDuration(mRecyclerView, animationType, animation.mTargetX - animation.mStartDx, animation.mTargetY - animation.mStartDy); animation.setDuration(duration); } } // removed from the list after it is drawn for the last time if (mOverdrawChild == prevSelected.itemView) { removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView); } } }; final long duration = mCallback.getAnimationDuration(mRecyclerView, animationType, targetTranslateX - currentTranslateX, targetTranslateY - currentTranslateY); rv.setDuration(duration); mRecoverAnimations.add(rv); rv.start(); mRecoverAnimations.add(animation); animation.start(); preventLayout = true; } else { removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView); Loading Loading @@ -715,7 +708,7 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration } @SuppressWarnings("WeakerAccess") /* synthetic access */ void postDispatchSwipe(final RecoverAnimation anim, final int swipeDir) { void postDispatchSwipe(final RecoverAnimation anim, final int swipeDir, final boolean moveBackAfterwards) { // wait until animations are complete. mRecyclerView.post(new Runnable() { @Override Loading @@ -731,6 +724,9 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration if ((animator == null || !animator.isRunning(null)) && !hasRunningRecoverAnim()) { mCallback.onSwiped(anim.mViewHolder, swipeDir); if (moveBackAfterwards) { startMoveBackAnimation(anim); } } else { mRecyclerView.post(this); } Loading @@ -739,6 +735,20 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration }); } private void startMoveBackAnimation(RecoverAnimation animation) { MoveBackAnimation moveBackAnimation = new MoveBackAnimation(animation.mViewHolder, animation.mAnimationType, animation.mActionState, animation.mTargetX, animation.mTargetY); long duration = mCallback.getAnimationDuration(mRecyclerView, animation.mAnimationType, -animation.mTargetX, -animation.mTargetY); moveBackAnimation.setDuration(duration); mRecoverAnimations.remove(animation); mRecoverAnimations.add(moveBackAnimation); moveBackAnimation.start(); } @SuppressWarnings("WeakerAccess") /* synthetic access */ boolean hasRunningRecoverAnim() { final int size = mRecoverAnimations.size(); Loading Loading @@ -2005,13 +2015,15 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration final ItemTouchHelper.RecoverAnimation anim = recoverAnimationList.get(i); anim.update(); final int count = c.save(); boolean isCurrentlyActive = anim instanceof MoveOutAnimation; boolean success = anim.mAnimationType == ANIMATION_TYPE_SWIPE_SUCCESS; onChildDraw(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState, anim.mAnimationType == ANIMATION_TYPE_SWIPE_SUCCESS && !anim.mIsPendingCleanup); isCurrentlyActive, success); c.restoreToCount(count); } if (selected != null) { final int count = c.save(); onChildDraw(c, parent, selected, dX, dY, actionState, true); onChildDraw(c, parent, selected, dX, dY, actionState, true, false); c.restoreToCount(count); } } Loading Loading @@ -2053,7 +2065,7 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration * This is a good place to clear all changes on the View that was done in * {@link #onSelectedChanged(RecyclerView.ViewHolder, int)}, * {@link #onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int, * boolean)} or * boolean, boolean)} or * {@link #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int, boolean)}. * * @param recyclerView The RecyclerView which is controlled by the ItemTouchHelper. Loading Loading @@ -2092,7 +2104,7 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration */ public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { float dX, float dY, int actionState, boolean isCurrentlyActive, boolean success) { ItemTouchUIUtilImpl.INSTANCE.onDraw(c, recyclerView, viewHolder.itemView, dX, dY, actionState, isCurrentlyActive); } Loading Loading @@ -2526,4 +2538,62 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration } } private class MoveBackAnimation extends RecoverAnimation { MoveBackAnimation(ViewHolder viewHolder, int animationType, int actionState, float startDx, float startDy) { super(viewHolder, animationType, actionState, startDx, startDy, /* targetX */ 0, /* targetY */ 0); } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (this.mOverridden) { return; } mCallback.clearView(mRecyclerView, mViewHolder); // full cleanup will happen on onDrawOver // removed from the list after it is drawn for the last time if (mOverdrawChild == mViewHolder.itemView) { removeChildDrawingOrderCallbackIfNecessary(mViewHolder.itemView); } } } private class MoveOutAnimation extends RecoverAnimation { private final int mSwipeDirection; private final boolean mMoveBackAfterwards; MoveOutAnimation(ViewHolder viewHolder, int animationType, int actionState, float startDx, float startDy, float targetX, float targetY, int swipeDirection, boolean moveBackAfterwards) { super(viewHolder, animationType, actionState, startDx, startDy, targetX, targetY); this.mSwipeDirection = swipeDirection; this.mMoveBackAfterwards = moveBackAfterwards; } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (this.mOverridden) { return; } if (!mMoveBackAfterwards) { mPendingCleanup.add(mViewHolder.itemView); } mIsPendingCleanup = true; // Animation might be ended by other animators during a layout. // We defer callback to avoid editing adapter during a layout. postDispatchSwipe(this, mSwipeDirection, mMoveBackAfterwards); if (!mMoveBackAfterwards) { // removed from the list after it is drawn for the last time if (mOverdrawChild == mViewHolder.itemView) { removeChildDrawingOrderCallbackIfNecessary(mViewHolder.itemView); } } } } }