Merge pull request #6470 from thundernest/swipe_select_state
Deselect message during swipe
This commit is contained in:
commit
23b68555fb
5 changed files with 298 additions and 109 deletions
|
@ -480,11 +480,11 @@ class MessageListAdapter internal constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun selectMessage(item: MessageListItem) {
|
||||
fun selectMessage(item: MessageListItem) {
|
||||
selected = selected + item.uniqueId
|
||||
}
|
||||
|
||||
private fun deselectMessage(item: MessageListItem) {
|
||||
fun deselectMessage(item: MessageListItem) {
|
||||
selected = selected - item.uniqueId
|
||||
}
|
||||
|
||||
|
|
|
@ -810,7 +810,24 @@ class MessageListFragment :
|
|||
|
||||
private fun toggleMessageSelect(messageListItem: MessageListItem) {
|
||||
adapter.toggleSelection(messageListItem)
|
||||
updateAfterSelectionChange()
|
||||
}
|
||||
|
||||
private fun selectMessage(messageListItem: MessageListItem) {
|
||||
adapter.selectMessage(messageListItem)
|
||||
updateAfterSelectionChange()
|
||||
}
|
||||
|
||||
private fun deselectMessage(messageListItem: MessageListItem) {
|
||||
adapter.deselectMessage(messageListItem)
|
||||
updateAfterSelectionChange()
|
||||
}
|
||||
|
||||
private fun isMessageSelected(messageListItem: MessageListItem): Boolean {
|
||||
return adapter.isSelected(messageListItem)
|
||||
}
|
||||
|
||||
private fun updateAfterSelectionChange() {
|
||||
if (adapter.selectedCount == 0) {
|
||||
actionMode?.finish()
|
||||
actionMode = null
|
||||
|
@ -1434,36 +1451,67 @@ class MessageListFragment :
|
|||
private val isPullToRefreshAllowed: Boolean
|
||||
get() = isRemoteSearchAllowed || isCheckMailAllowed
|
||||
|
||||
private val swipeListener = MessageListSwipeListener { item, action ->
|
||||
when (action) {
|
||||
SwipeAction.None -> Unit
|
||||
SwipeAction.ToggleSelection -> {
|
||||
toggleMessageSelect(item)
|
||||
private var itemSelectedOnSwipeStart = false
|
||||
|
||||
private val swipeListener = object : MessageListSwipeListener {
|
||||
override fun onSwipeStarted(item: MessageListItem, action: SwipeAction) {
|
||||
itemSelectedOnSwipeStart = isMessageSelected(item)
|
||||
if (itemSelectedOnSwipeStart && action != SwipeAction.ToggleSelection) {
|
||||
deselectMessage(item)
|
||||
}
|
||||
SwipeAction.ToggleRead -> {
|
||||
setFlag(item, Flag.SEEN, !item.isRead)
|
||||
}
|
||||
SwipeAction.ToggleStar -> {
|
||||
setFlag(item, Flag.FLAGGED, !item.isStarred)
|
||||
}
|
||||
SwipeAction.Archive -> {
|
||||
onArchive(item.messageReference)
|
||||
}
|
||||
SwipeAction.Delete -> {
|
||||
if (K9.isConfirmDelete) {
|
||||
notifyItemChanged(item)
|
||||
}
|
||||
|
||||
override fun onSwipeActionChanged(item: MessageListItem, action: SwipeAction) {
|
||||
if (action == SwipeAction.ToggleSelection) {
|
||||
if (itemSelectedOnSwipeStart && !isMessageSelected(item)) {
|
||||
selectMessage(item)
|
||||
}
|
||||
onDelete(listOf(item.messageReference))
|
||||
} else if (isMessageSelected(item)) {
|
||||
deselectMessage(item)
|
||||
}
|
||||
SwipeAction.Spam -> {
|
||||
if (K9.isConfirmSpam) {
|
||||
notifyItemChanged(item)
|
||||
}
|
||||
|
||||
override fun onSwipeAction(item: MessageListItem, action: SwipeAction) {
|
||||
if (action.removesItem || action == SwipeAction.ToggleSelection) {
|
||||
itemSelectedOnSwipeStart = false
|
||||
}
|
||||
|
||||
when (action) {
|
||||
SwipeAction.None -> Unit
|
||||
SwipeAction.ToggleSelection -> {
|
||||
toggleMessageSelect(item)
|
||||
}
|
||||
SwipeAction.ToggleRead -> {
|
||||
setFlag(item, Flag.SEEN, !item.isRead)
|
||||
}
|
||||
SwipeAction.ToggleStar -> {
|
||||
setFlag(item, Flag.FLAGGED, !item.isStarred)
|
||||
}
|
||||
SwipeAction.Archive -> {
|
||||
onArchive(item.messageReference)
|
||||
}
|
||||
SwipeAction.Delete -> {
|
||||
if (K9.isConfirmDelete) {
|
||||
notifyItemChanged(item)
|
||||
}
|
||||
onDelete(listOf(item.messageReference))
|
||||
}
|
||||
SwipeAction.Spam -> {
|
||||
if (K9.isConfirmSpam) {
|
||||
notifyItemChanged(item)
|
||||
}
|
||||
onSpam(listOf(item.messageReference))
|
||||
}
|
||||
SwipeAction.Move -> {
|
||||
notifyItemChanged(item)
|
||||
onMove(item.messageReference)
|
||||
}
|
||||
onSpam(listOf(item.messageReference))
|
||||
}
|
||||
SwipeAction.Move -> {
|
||||
notifyItemChanged(item)
|
||||
onMove(item.messageReference)
|
||||
}
|
||||
|
||||
override fun onSwipeEnded(item: MessageListItem) {
|
||||
if (itemSelectedOnSwipeStart && !isMessageSelected(item)) {
|
||||
selectMessage(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,12 +68,30 @@ class MessageListSwipeCallback(
|
|||
throw UnsupportedOperationException("not implemented")
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
|
||||
val holder = viewHolder as MessageViewHolder
|
||||
val item = adapter.getItemById(holder.uniqueId) ?: error("Couldn't find MessageListItem")
|
||||
override fun onSwipeStarted(viewHolder: ViewHolder, direction: Int) {
|
||||
val swipeAction = when (direction) {
|
||||
ItemTouchHelper.RIGHT -> swipeRightAction
|
||||
ItemTouchHelper.LEFT -> swipeLeftAction
|
||||
else -> error("Unsupported direction: $direction")
|
||||
}
|
||||
|
||||
// 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.
|
||||
listener.onSwipeStarted(viewHolder.messageListItem, swipeAction)
|
||||
}
|
||||
|
||||
override fun onSwipeDirectionChanged(viewHolder: ViewHolder, direction: Int) {
|
||||
val swipeAction = when (direction) {
|
||||
ItemTouchHelper.RIGHT -> swipeRightAction
|
||||
ItemTouchHelper.LEFT -> swipeLeftAction
|
||||
else -> error("Unsupported direction: $direction")
|
||||
}
|
||||
|
||||
listener.onSwipeActionChanged(viewHolder.messageListItem, swipeAction)
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
|
||||
val item = viewHolder.messageListItem
|
||||
|
||||
// Mark view to prevent MessageListItemAnimator from interfering with swipe animations
|
||||
viewHolder.markAsSwiped(true)
|
||||
|
||||
when (direction) {
|
||||
|
@ -83,6 +101,10 @@ class MessageListSwipeCallback(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onSwipeEnded(viewHolder: ViewHolder) {
|
||||
listener.onSwipeEnded(viewHolder.messageListItem)
|
||||
}
|
||||
|
||||
override fun clearView(recyclerView: RecyclerView, viewHolder: ViewHolder) {
|
||||
super.clearView(recyclerView, viewHolder)
|
||||
viewHolder.markAsSwiped(false)
|
||||
|
@ -99,26 +121,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) {
|
||||
|
@ -208,14 +229,21 @@ class MessageListSwipeCallback(
|
|||
val percentage = abs(animateDx) / recyclerView.width
|
||||
return (super.getAnimationDuration(recyclerView, animationType, animateDx, animateDy) * percentage).toLong()
|
||||
}
|
||||
|
||||
private val ViewHolder.messageListItem: MessageListItem
|
||||
get() = (this as? MessageViewHolder)?.uniqueId?.let { adapter.getItemById(it) }
|
||||
?: error("Couldn't find MessageListItem")
|
||||
}
|
||||
|
||||
fun interface SwipeActionSupportProvider {
|
||||
fun isActionSupported(item: MessageListItem, action: SwipeAction): Boolean
|
||||
}
|
||||
|
||||
fun interface MessageListSwipeListener {
|
||||
interface MessageListSwipeListener {
|
||||
fun onSwipeStarted(item: MessageListItem, action: SwipeAction)
|
||||
fun onSwipeActionChanged(item: MessageListItem, action: SwipeAction)
|
||||
fun onSwipeAction(item: MessageListItem, action: SwipeAction)
|
||||
fun onSwipeEnded(item: MessageListItem)
|
||||
}
|
||||
|
||||
private fun ViewHolder.markAsSwiped(value: Boolean) {
|
||||
|
|
|
@ -193,6 +193,11 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
|
|||
|
||||
float mDy;
|
||||
|
||||
/**
|
||||
* Current swipe direction. Used for {@link Callback#onSwipeDirectionChanged(ViewHolder, int)}
|
||||
*/
|
||||
private int mSwipeDirection = 0;
|
||||
|
||||
/**
|
||||
* The coordinates of the selected view at the time it is selected. We record these values
|
||||
* when action starts so that we can consistently position it even if LayoutManager moves the
|
||||
|
@ -554,6 +559,14 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
|
|||
if ((mSelectedFlags & (LEFT | RIGHT)) != 0 && dx != 0) {
|
||||
dx = limitDeltaX(parent, dx);
|
||||
}
|
||||
|
||||
if (dx != 0) {
|
||||
int direction = dx > 0 ? RIGHT : LEFT;
|
||||
if (direction != mSwipeDirection) {
|
||||
mSwipeDirection = direction;
|
||||
mCallback.onSwipeDirectionChanged(mSelected, direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mCallback.onDraw(c, parent, mSelected,
|
||||
|
@ -582,6 +595,7 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
|
|||
if (selected == mSelected && actionState == mActionState) {
|
||||
return;
|
||||
}
|
||||
|
||||
mDragScrollStartTimeInMs = Long.MIN_VALUE;
|
||||
final int prevActionState = mActionState;
|
||||
// prevent duplicate animations
|
||||
|
@ -615,33 +629,7 @@ 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;
|
||||
switch (swipeDir) {
|
||||
case LEFT:
|
||||
case RIGHT:
|
||||
case START:
|
||||
case END:
|
||||
if (mCallback.shouldAnimateOut(swipeDir)) {
|
||||
targetTranslateX = Math.signum(mDx) * mRecyclerView.getWidth();
|
||||
} else if (wasFling) {
|
||||
int maxSwipeDistance = mCallback.getMaxSwipeDistance(mRecyclerView, swipeDir);
|
||||
targetTranslateX = Math.signum(mDx) * maxSwipeDistance;
|
||||
} else {
|
||||
targetTranslateX = currentTranslateX;
|
||||
}
|
||||
targetTranslateY = 0;
|
||||
break;
|
||||
case UP:
|
||||
case DOWN:
|
||||
targetTranslateX = 0;
|
||||
targetTranslateY = Math.signum(mDy) * mRecyclerView.getHeight();
|
||||
break;
|
||||
default:
|
||||
targetTranslateX = 0;
|
||||
targetTranslateY = 0;
|
||||
}
|
||||
final int animationType;
|
||||
if (prevActionState == ACTION_STATE_DRAG) {
|
||||
animationType = ANIMATION_TYPE_DRAG;
|
||||
} else if (swipeDir > 0) {
|
||||
|
@ -650,40 +638,59 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
|
|||
animationType = ANIMATION_TYPE_SWIPE_CANCEL;
|
||||
}
|
||||
|
||||
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
|
||||
final RecoverAnimation animation;
|
||||
final boolean useDefaultDuration;
|
||||
switch (swipeDir) {
|
||||
case LEFT:
|
||||
case RIGHT:
|
||||
case START:
|
||||
case END:
|
||||
if (mCallback.shouldAnimateOut(swipeDir)) {
|
||||
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);
|
||||
float targetTranslateX = Math.signum(mDx) * maxSwipeDistance;
|
||||
|
||||
animation = new MoveOutAnimation(prevSelected, animationType, prevActionState,
|
||||
currentTranslateX, currentTranslateY, targetTranslateX, currentTranslateY, swipeDir,
|
||||
/* moveBackAfterwards */ true);
|
||||
|
||||
useDefaultDuration = true;
|
||||
} 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);
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// 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();
|
||||
break;
|
||||
case UP:
|
||||
case DOWN:
|
||||
throw new UnsupportedOperationException();
|
||||
default:
|
||||
animation = new MoveBackAnimation(prevSelected, animationType, prevActionState,
|
||||
currentTranslateX, currentTranslateY);
|
||||
useDefaultDuration = true;
|
||||
}
|
||||
|
||||
if (useDefaultDuration) {
|
||||
long duration = mCallback.getAnimationDuration(mRecyclerView, animationType,
|
||||
animation.mTargetX - animation.mStartDx, animation.mTargetY - animation.mStartDy);
|
||||
animation.setDuration(duration);
|
||||
}
|
||||
|
||||
mRecoverAnimations.add(animation);
|
||||
animation.start();
|
||||
|
||||
preventLayout = true;
|
||||
} else {
|
||||
removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);
|
||||
|
@ -715,7 +722,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
|
||||
|
@ -731,6 +738,11 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
|
|||
if ((animator == null || !animator.isRunning(null))
|
||||
&& !hasRunningRecoverAnim()) {
|
||||
mCallback.onSwiped(anim.mViewHolder, swipeDir);
|
||||
if (moveBackAfterwards) {
|
||||
startMoveBackAnimation(anim);
|
||||
} else {
|
||||
mCallback.onSwipeEnded(anim.mViewHolder);
|
||||
}
|
||||
} else {
|
||||
mRecyclerView.post(this);
|
||||
}
|
||||
|
@ -739,6 +751,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();
|
||||
|
@ -1051,6 +1077,10 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
|
|||
}
|
||||
mDx = mDy = 0f;
|
||||
mActivePointerId = motionEvent.getPointerId(0);
|
||||
|
||||
mSwipeDirection = dx > 0 ? RIGHT : LEFT;
|
||||
mCallback.onSwipeStarted(vh, mSwipeDirection);
|
||||
|
||||
select(vh, ACTION_STATE_SWIPE);
|
||||
}
|
||||
|
||||
|
@ -2005,13 +2035,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);
|
||||
}
|
||||
}
|
||||
|
@ -2053,7 +2085,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.
|
||||
|
@ -2092,7 +2124,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);
|
||||
}
|
||||
|
@ -2228,9 +2260,18 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
|
|||
* @param direction The swipe direction.
|
||||
* @return The maximum distance in pixels that a view can be moved during a swipe.
|
||||
*/
|
||||
public int getMaxSwipeDistance(RecyclerView recyclerView, int direction) {
|
||||
public int getMaxSwipeDistance(@NonNull RecyclerView recyclerView, int direction) {
|
||||
return recyclerView.getWidth();
|
||||
}
|
||||
|
||||
public void onSwipeStarted(@NonNull ViewHolder viewHolder, int direction) {
|
||||
}
|
||||
|
||||
public void onSwipeDirectionChanged(@NonNull ViewHolder viewHolder, int direction) {
|
||||
}
|
||||
|
||||
public void onSwipeEnded(@NonNull ViewHolder viewHolder) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2526,4 +2567,64 @@ 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.onSwipeEnded(mViewHolder);
|
||||
|
||||
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…
Reference in a new issue