Java tutorial
/* * Copyright (C) 2015 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package vc908.stickerfactory.ui.advancedrecyclerview.swipeable; import android.graphics.Rect; import android.os.Build; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPropertyAnimatorListener; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import vc908.stickerfactory.ui.advancedrecyclerview.animator.SwipeDismissItemAnimator; import vc908.stickerfactory.ui.advancedrecyclerview.utils.CustomRecyclerViewUtils; import vc908.stickerfactory.ui.advancedrecyclerview.utils.WrapperAdapterUtils; /** * Provides item swipe operation for {@link RecyclerView} */ @SuppressWarnings("PointlessBitwiseExpression") public class RecyclerViewSwipeManager { private static final String TAG = "ARVSwipeManager"; /** * Result code of swipe operation. Used for the second argument of the * {@link SwipeableItemAdapter#onPerformAfterSwipeReaction(RecyclerView.ViewHolder, int, int, int)} method. * * None. (internal default value, this value is not used for the argument) */ public static final int RESULT_NONE = 0; /** * Result code of swipe operation. Used for the second argument of the * {@link SwipeableItemAdapter#onPerformAfterSwipeReaction(RecyclerView.ViewHolder, int, int, int)} method. * * Canceled. */ public static final int RESULT_CANCELED = 1; /** * Result code of swipe operation. Used for the second argument of the * {@link SwipeableItemAdapter#onPerformAfterSwipeReaction(RecyclerView.ViewHolder, int, int, int)} method. * * Swipe left performed. */ public static final int RESULT_SWIPED_LEFT = 2; /** * Result code of swipe operation. Used for the second argument of the * {@link SwipeableItemAdapter#onPerformAfterSwipeReaction(RecyclerView.ViewHolder, int, int, int)} method. * * Swipe right performed. */ public static final int RESULT_SWIPED_RIGHT = 3; // --- /** * Reaction type to swipe operation. Used for the return value of the * {@link SwipeableItemAdapter#onGetSwipeReactionType(RecyclerView.ViewHolder, int, int, int)} method. * * Indicates "can not swipe left" (completely no reactions) */ public static final int REACTION_CAN_NOT_SWIPE_LEFT = (0 << 0); /** * Reaction type to swipe operation. Used for the return value of the * {@link SwipeableItemAdapter#onGetSwipeReactionType(RecyclerView.ViewHolder, int, int, int)} method. * * Indicates "can not swipe left" (not swipeable, but rubber-band effect applied) */ public static final int REACTION_CAN_NOT_SWIPE_LEFT_WITH_RUBBER_BAND_EFFECT = (1 << 0); /** * Reaction type to swipe operation. Used for the return value of the * {@link SwipeableItemAdapter#onGetSwipeReactionType(RecyclerView.ViewHolder, int, int, int)} method. * * Indicates "can swipe left" */ public static final int REACTION_CAN_SWIPE_LEFT = (2 << 0); /** * Reaction type to swipe operation. Used for the return value of the * {@link SwipeableItemAdapter#onGetSwipeReactionType(RecyclerView.ViewHolder, int, int, int)} method. * * Indicates "can not swipe right" (completely no reactions) */ public static final int REACTION_CAN_NOT_SWIPE_RIGHT = (0 << 16); /** * Reaction type to swipe operation. Used for the return value of the * {@link SwipeableItemAdapter#onGetSwipeReactionType(RecyclerView.ViewHolder, int, int, int)} method. * * Indicates "can not swipe right" (not swipeable, but rubber-band effect applied) */ public static final int REACTION_CAN_NOT_SWIPE_RIGHT_WITH_RUBBER_BAND_EFFECT = (1 << 16); /** * Reaction type to swipe operation. Used for the return value of the * {@link SwipeableItemAdapter#onGetSwipeReactionType(RecyclerView.ViewHolder, int, int, int)} method. * * Indicates "can swipe right" */ public static final int REACTION_CAN_SWIPE_RIGHT = (2 << 16); /** * Convenient constant value: Equals to {@link #REACTION_CAN_NOT_SWIPE_LEFT} | {@link #REACTION_CAN_NOT_SWIPE_RIGHT} */ public static final int REACTION_CAN_NOT_SWIPE_BOTH = REACTION_CAN_NOT_SWIPE_LEFT | REACTION_CAN_NOT_SWIPE_RIGHT; /** * Convenient constant value: Equals to {@link #REACTION_CAN_NOT_SWIPE_LEFT_WITH_RUBBER_BAND_EFFECT} | {@link #REACTION_CAN_NOT_SWIPE_RIGHT_WITH_RUBBER_BAND_EFFECT} */ public static final int REACTION_CAN_NOT_SWIPE_BOTH_WITH_RUBBER_BAND_EFFECT = REACTION_CAN_NOT_SWIPE_LEFT_WITH_RUBBER_BAND_EFFECT | REACTION_CAN_NOT_SWIPE_RIGHT_WITH_RUBBER_BAND_EFFECT; /** * Convenient constant value: Equals to {@link #REACTION_CAN_SWIPE_LEFT} | {@link #REACTION_CAN_SWIPE_RIGHT} */ public static final int REACTION_CAN_SWIPE_BOTH = REACTION_CAN_SWIPE_LEFT | REACTION_CAN_SWIPE_RIGHT; // --- /** * Background drawable type used for the second argument of the * {@link SwipeableItemAdapter#onSetSwipeBackground(RecyclerView.ViewHolder, int, int)} method. * * Background image for the neutral (= not swiping) item. */ public static final int DRAWABLE_SWIPE_NEUTRAL_BACKGROUND = 0; /** * Background drawable type used for the second argument of the * {@link SwipeableItemAdapter#onSetSwipeBackground(RecyclerView.ViewHolder, int, int)} method. * * Background image for the swiping-left item. */ public static final int DRAWABLE_SWIPE_LEFT_BACKGROUND = 1; /** * Background drawable type used for the second argument of the * {@link SwipeableItemAdapter#onSetSwipeBackground(RecyclerView.ViewHolder, int, int)} method. * * Background image for the swiping-right item. */ public static final int DRAWABLE_SWIPE_RIGHT_BACKGROUND = 2; // --- /** * After-reaction type used for the {@link SwipeableItemViewHolder#setAfterSwipeReaction(int)} and {@link SwipeableItemViewHolder#getAfterSwipeReaction()} methods. * Represents perform nothing. */ public static final int AFTER_SWIPE_REACTION_DEFAULT = 0; /** * After-reaction type used for the {@link SwipeableItemViewHolder#setAfterSwipeReaction(int)} and {@link SwipeableItemViewHolder#getAfterSwipeReaction()} methods. * Represents remove the swiped item. */ public static final int AFTER_SWIPE_REACTION_REMOVE_ITEM = 1; /** * After-reaction type used for the {@link SwipeableItemViewHolder#setAfterSwipeReaction(int)} and {@link SwipeableItemViewHolder#getAfterSwipeReaction()} methods. * Represents that the item moved to swiped direction. */ public static final int AFTER_SWIPE_REACTION_MOVE_TO_SWIPED_DIRECTION = 2; // --- /** * Special value for the {@link SwipeableItemViewHolder#setSwipeItemSlideAmount(float)} and {@link SwipeableItemViewHolder#getSwipeItemSlideAmount()} methods. * Indicates that this item is pinned to LEFT of the window. */ public static final float OUTSIDE_OF_THE_WINDOW_LEFT = -Float.MAX_VALUE; /** * Special value for the {@link SwipeableItemViewHolder#setSwipeItemSlideAmount(float)} and {@link SwipeableItemViewHolder#getSwipeItemSlideAmount()} methods. * Indicates that this item is pinned to RIGHT the window. */ public static final float OUTSIDE_OF_THE_WINDOW_RIGHT = Float.MAX_VALUE; // --- /** * State flag for the {@link SwipeableItemViewHolder#setSwipeStateFlags(int)} and {@link SwipeableItemViewHolder#getSwipeStateFlags()} methods. * Indicates that currently performing swiping. */ public static final int STATE_FLAG_SWIPING = (1 << 0); /** * State flag for the {@link SwipeableItemViewHolder#setSwipeStateFlags(int)} and {@link SwipeableItemViewHolder#getSwipeStateFlags()} methods. * Indicates that this item is being swiped. */ public static final int STATE_FLAG_IS_ACTIVE = (1 << 1); /** * State flag for the {@link SwipeableItemViewHolder#setSwipeStateFlags(int)} and {@link SwipeableItemViewHolder#getSwipeStateFlags()} methods. * If this flag is set, some other flags are changed and require to apply. */ public static final int STATE_FLAG_IS_UPDATED = (1 << 31); // --- private static final int MIN_DISTANCE_TOUCH_SLOP_MUL = 10; private static final int SLIDE_ITEM_IMMEDIATELY_SET_TRANSLATION_THRESHOLD_DP = 8; private static final boolean LOCAL_LOGV = false; private static final boolean LOCAL_LOGD = false; private RecyclerView.OnItemTouchListener mInternalUseOnItemTouchListener; private RecyclerView mRecyclerView; private long mReturnToDefaultPositionAnimationDuration = 300; private long mMoveToOutsideWindowAnimationDuration = 200; private int mTouchSlop; private int mMinFlingVelocity; // [pixels per second] private int mMaxFlingVelocity; // [pixels per second] private int mInitialTouchX; private int mInitialTouchY; private long mCheckingTouchSlop = RecyclerView.NO_ID; private ItemSlidingAnimator mItemSlideAnimator; private SwipeableItemWrapperAdapter<RecyclerView.ViewHolder> mAdapter; private RecyclerView.ViewHolder mSwipingItem; private int mSwipingItemPosition = RecyclerView.NO_POSITION; private Rect mSwipingItemMargins = new Rect(); private int mTouchedItemOffsetX; private int mLastTouchX; private int mSwipingItemReactionType; private VelocityTracker mVelocityTracker; private SwipingItemOperator mSwipingItemOperator; /** * Constructor. */ public RecyclerViewSwipeManager() { mInternalUseOnItemTouchListener = new RecyclerView.OnItemTouchListener() { @Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { return RecyclerViewSwipeManager.this.onInterceptTouchEvent(rv, e); } @Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { RecyclerViewSwipeManager.this.onTouchEvent(rv, e); } @Override public void onRequestDisallowInterceptTouchEvent(boolean arg0) { // TODO Auto-generated method stub } }; mItemSlideAnimator = new ItemSlidingAnimator(); mVelocityTracker = VelocityTracker.obtain(); } /** * Create wrapped adapter. * * @param adapter The target adapter. * * @return Wrapped adapter which is associated to this {@link RecyclerViewSwipeManager} instance. */ @SuppressWarnings("unchecked") public RecyclerView.Adapter createWrappedAdapter(RecyclerView.Adapter adapter) { if (mAdapter != null) { throw new IllegalStateException("already have a wrapped adapter"); } mAdapter = new SwipeableItemWrapperAdapter(this, adapter); return mAdapter; } /** * Indicates this manager instance has released or not. * * @return True if this manager instance has released */ public boolean isReleased() { return (mInternalUseOnItemTouchListener == null); } /** * Attaches {@link RecyclerView} instance. * * Before calling this method, the target {@link RecyclerView} must set * the wrapped adapter instance which is returned by the * {@link #createWrappedAdapter(RecyclerView.Adapter)} method. * * @param rv The {@link RecyclerView} instance */ public void attachRecyclerView(RecyclerView rv) { if (rv == null) { throw new IllegalArgumentException("RecyclerView cannot be null"); } if (isReleased()) { throw new IllegalStateException("Accessing released object"); } if (mRecyclerView != null) { throw new IllegalStateException("RecyclerView instance has already been set"); } if (mAdapter == null || getSwipeableItemWrapperAdapter(rv) != mAdapter) { throw new IllegalStateException("adapter is not set properly"); } mRecyclerView = rv; mRecyclerView.addOnItemTouchListener(mInternalUseOnItemTouchListener); final ViewConfiguration vc = ViewConfiguration.get(rv.getContext()); mTouchSlop = vc.getScaledTouchSlop(); mMinFlingVelocity = vc.getScaledMinimumFlingVelocity(); mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); mItemSlideAnimator .setImmediatelySetTranslationThreshold((int) (rv.getResources().getDisplayMetrics().density * SLIDE_ITEM_IMMEDIATELY_SET_TRANSLATION_THRESHOLD_DP + 0.5f)); } /** * Detach the {@link RecyclerView} instance and release internal field references. * * This method should be called in order to avoid memory leaks. */ public void release() { if (mRecyclerView != null && mInternalUseOnItemTouchListener != null) { mRecyclerView.removeOnItemTouchListener(mInternalUseOnItemTouchListener); } mInternalUseOnItemTouchListener = null; if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } if (mItemSlideAnimator != null) { mItemSlideAnimator.endAnimations(); mItemSlideAnimator = null; } mAdapter = null; mRecyclerView = null; } /** * Indicates whether currently performing item swiping. * * @return True if currently performing item swiping */ public boolean isSwiping() { return (mSwipingItem != null); } /*package*/ boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { final int action = MotionEventCompat.getActionMasked(e); if (LOCAL_LOGV) { Log.v(TAG, "onInterceptTouchEvent() action = " + action); } switch (action) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (isSwiping()) { handleActionUpOrCancelWhileSwiping(e); return true; } else { handleActionUpOrCancelWhileNotSwiping(); } break; case MotionEvent.ACTION_DOWN: if (!isSwiping()) { handleActionDown(rv, e); } break; case MotionEvent.ACTION_MOVE: if (isSwiping()) { // NOTE: The first ACTION_MOVE event will come here. (maybe a bug of RecyclerView?) handleActionMoveWhileSwiping(e); return true; } else { if (handleActionMoveWhileNotSwiping(rv, e)) { return true; } } break; } return false; } /*package*/ void onTouchEvent(RecyclerView rv, MotionEvent e) { final int action = MotionEventCompat.getActionMasked(e); if (LOCAL_LOGV) { Log.v(TAG, "onTouchEvent() action = " + action); } if (!isSwiping()) { return; } switch (action) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: handleActionUpOrCancelWhileSwiping(e); break; case MotionEvent.ACTION_MOVE: handleActionMoveWhileSwiping(e); break; } } private boolean handleActionDown(RecyclerView rv, MotionEvent e) { final RecyclerView.Adapter adapter = rv.getAdapter(); final RecyclerView.ViewHolder holder = CustomRecyclerViewUtils.findChildViewHolderUnderWithTranslation(rv, e.getX(), e.getY()); if (!(holder instanceof SwipeableItemViewHolder)) { return false; } final int itemPosition = CustomRecyclerViewUtils.getSynchronizedPosition(holder); // verify the touched item is valid state if (!(itemPosition >= 0 && itemPosition < adapter.getItemCount())) { return false; } if (holder.getItemId() != adapter.getItemId(itemPosition)) { return false; } final int touchX = (int) (e.getX() + 0.5f); final int touchY = (int) (e.getY() + 0.5f); final View view = holder.itemView; final int translateX = (int) (ViewCompat.getTranslationX(view) + 0.5f); final int translateY = (int) (ViewCompat.getTranslationY(view) + 0.5f); final int viewX = touchX - (view.getLeft() + translateX); final int viewY = touchY - (view.getTop() + translateY); final int reactionType = mAdapter.getSwipeReactionType(holder, itemPosition, viewX, viewY); if (reactionType == 0) { return false; } mInitialTouchX = touchX; mInitialTouchY = touchY; mCheckingTouchSlop = holder.getItemId(); mSwipingItemReactionType = reactionType; return true; } private static SwipeableItemWrapperAdapter getSwipeableItemWrapperAdapter(RecyclerView rv) { return WrapperAdapterUtils.findWrappedAdapter(rv.getAdapter(), SwipeableItemWrapperAdapter.class); } private void handleActionUpOrCancelWhileNotSwiping() { mCheckingTouchSlop = RecyclerView.NO_ID; mSwipingItemReactionType = 0; } private boolean handleActionUpOrCancelWhileSwiping(MotionEvent e) { mLastTouchX = (int) (e.getX() + 0.5f); int result = RESULT_CANCELED; if (MotionEventCompat.getActionMasked(e) == MotionEvent.ACTION_UP) { final int viewWidth = mSwipingItem.itemView.getWidth(); final float distance = mLastTouchX - mInitialTouchX; final float absDistance = Math.abs(distance); mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity); // 1000: pixels per second final float xVelocity = mVelocityTracker.getXVelocity(); final float absXVelocity = Math.abs(xVelocity); if ((absDistance > (mTouchSlop * MIN_DISTANCE_TOUCH_SLOP_MUL)) && ((distance * xVelocity) > 0.0f) && (absXVelocity <= mMaxFlingVelocity) && ((absDistance > (viewWidth / 2)) || (absXVelocity >= mMinFlingVelocity))) { if ((distance < 0) && SwipeReactionUtils.canSwipeLeft(mSwipingItemReactionType)) { result = RESULT_SWIPED_LEFT; } else if ((distance > 0) && SwipeReactionUtils.canSwipeRight(mSwipingItemReactionType)) { result = RESULT_SWIPED_RIGHT; } } } if (LOCAL_LOGD) { Log.d(TAG, "swping finished --- result = " + result); } finishSwiping(result); return true; } private boolean handleActionMoveWhileNotSwiping(RecyclerView rv, MotionEvent e) { if (mCheckingTouchSlop == RecyclerView.NO_ID) { return false; } if (Math.abs(e.getY() - mInitialTouchY) > mTouchSlop) { // scrolling occurred mCheckingTouchSlop = RecyclerView.NO_ID; return false; } if (Math.abs(e.getX() - mInitialTouchX) <= mTouchSlop) { return false; } final RecyclerView.ViewHolder holder = CustomRecyclerViewUtils.findChildViewHolderUnderWithTranslation(rv, e.getX(), e.getY()); if (holder == null || holder.getItemId() != mCheckingTouchSlop) { mCheckingTouchSlop = RecyclerView.NO_ID; return false; } final int itemPosition = CustomRecyclerViewUtils.getSynchronizedPosition(holder); if (itemPosition == RecyclerView.NO_POSITION) { return false; } if (LOCAL_LOGD) { Log.d(TAG, "swiping started"); } startSwiping(rv, e, holder, itemPosition); return true; } private void handleActionMoveWhileSwiping(MotionEvent e) { mLastTouchX = (int) (e.getX() + 0.5f); mVelocityTracker.addMovement(e); final int swipeDistance = mLastTouchX - mTouchedItemOffsetX; mSwipingItemOperator.update(swipeDistance); } private void startSwiping(RecyclerView rv, MotionEvent e, RecyclerView.ViewHolder holder, int itemPosition) { mSwipingItem = holder; mSwipingItemPosition = itemPosition; mLastTouchX = (int) (e.getX() + 0.5f); mTouchedItemOffsetX = mLastTouchX; CustomRecyclerViewUtils.getLayoutMargins(holder.itemView, mSwipingItemMargins); mSwipingItemOperator = new SwipingItemOperator(this, mSwipingItem, mSwipingItemPosition, mSwipingItemReactionType); mSwipingItemOperator.start(); mVelocityTracker.clear(); mVelocityTracker.addMovement(e); mRecyclerView.getParent().requestDisallowInterceptTouchEvent(true); // raise onSwipeItemStarted() event mAdapter.onSwipeItemStarted(this, holder, itemPosition); } private void finishSwiping(int result) { final RecyclerView.ViewHolder swipingItem = mSwipingItem; if (swipingItem == null) { return; } if (mRecyclerView != null && mRecyclerView.getParent() != null) { mRecyclerView.getParent().requestDisallowInterceptTouchEvent(false); } final int itemPosition = mSwipingItemPosition; mVelocityTracker.clear(); mSwipingItem = null; mSwipingItemPosition = RecyclerView.NO_POSITION; mLastTouchX = 0; mInitialTouchX = 0; mTouchedItemOffsetX = 0; mCheckingTouchSlop = RecyclerView.NO_ID; mSwipingItemReactionType = 0; if (mSwipingItemOperator != null) { mSwipingItemOperator.finish(); mSwipingItemOperator = null; } final boolean toLeft = (result == RESULT_SWIPED_LEFT); int afterReaction = AFTER_SWIPE_REACTION_DEFAULT; if (mAdapter != null) { afterReaction = mAdapter.onSwipeItemFinished(swipingItem, itemPosition, result); } afterReaction = correctAfterReaction(result, afterReaction); switch (afterReaction) { case AFTER_SWIPE_REACTION_DEFAULT: mItemSlideAnimator.slideToDefaultPosition(swipingItem, true, mReturnToDefaultPositionAnimationDuration); break; case AFTER_SWIPE_REACTION_MOVE_TO_SWIPED_DIRECTION: mItemSlideAnimator.slideToOutsideOfWindow(swipingItem, toLeft, true, mMoveToOutsideWindowAnimationDuration); break; case AFTER_SWIPE_REACTION_REMOVE_ITEM: { final RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator(); final long removeAnimationDuration = (itemAnimator != null) ? itemAnimator.getRemoveDuration() : 0; if (supportsViewPropertyAnimator()) { final long moveAnimationDuration = (itemAnimator != null) ? itemAnimator.getMoveDuration() : 0; final RemovingItemDecorator decorator = new RemovingItemDecorator(mRecyclerView, swipingItem, removeAnimationDuration, moveAnimationDuration); decorator.setMoveAnimationInterpolator(SwipeDismissItemAnimator.MOVE_INTERPOLATOR); decorator.start(); } mItemSlideAnimator.slideToOutsideOfWindow(swipingItem, toLeft, true, removeAnimationDuration); } break; default: throw new IllegalStateException("Unknwon after reaction type: " + afterReaction); } if (mAdapter != null) { mAdapter.onSwipeItemFinished2(swipingItem, itemPosition, result, afterReaction); } } private static int correctAfterReaction(int result, int afterReaction) { if ((afterReaction == AFTER_SWIPE_REACTION_MOVE_TO_SWIPED_DIRECTION) || (afterReaction == AFTER_SWIPE_REACTION_REMOVE_ITEM)) { if (!((result == RESULT_SWIPED_LEFT) || (result == RESULT_SWIPED_RIGHT))) { afterReaction = AFTER_SWIPE_REACTION_DEFAULT; } } return afterReaction; } public void cancelSwipe() { finishSwiping(RESULT_CANCELED); } /*package*/ boolean isAnimationRunning(RecyclerView.ViewHolder item) { return (mItemSlideAnimator != null) && (mItemSlideAnimator.isRunning(item)); } private void slideItem(RecyclerView.ViewHolder holder, float amount, boolean shouldAnimate) { if (amount == OUTSIDE_OF_THE_WINDOW_LEFT) { mItemSlideAnimator.slideToOutsideOfWindow(holder, true, shouldAnimate, mMoveToOutsideWindowAnimationDuration); } else if (amount == OUTSIDE_OF_THE_WINDOW_RIGHT) { mItemSlideAnimator.slideToOutsideOfWindow(holder, false, shouldAnimate, mMoveToOutsideWindowAnimationDuration); } else if (amount == 0.0f) { mItemSlideAnimator.slideToDefaultPosition(holder, shouldAnimate, mReturnToDefaultPositionAnimationDuration); } else { mItemSlideAnimator.slideToSpecifiedPosition(holder, amount, shouldAnimate); } } /** * Gets the duration of the "return to default position" animation * * @return Duration of the "return to default position" animation in milliseconds */ public long getReturnToDefaultPositionAnimationDuration() { return mReturnToDefaultPositionAnimationDuration; } /** * Sets the duration of the "return to default position" animation * * @param duration Duration of the "return to default position" animation in milliseconds */ public void setReturnToDefaultPositionAnimationDuration(long duration) { mReturnToDefaultPositionAnimationDuration = duration; } /** * Gets the duration of the "move to outside of the window" animation * * @return Duration of the "move to outside of the window" animation in milliseconds */ public long getMoveToOutsideWindowAnimationDuration() { return mMoveToOutsideWindowAnimationDuration; } /** * Sets the duration of the "move to outside of the window" animation * * @param duration Duration of the "move to outside of the window" animation in milliseconds */ public void setMoveToOutsideWindowAnimationDuration(long duration) { mMoveToOutsideWindowAnimationDuration = duration; } /*package*/ void applySlideItem(RecyclerView.ViewHolder holder, int itemPosition, float prevAmount, float amount, boolean shouldAnimate) { final SwipeableItemViewHolder holder2 = (SwipeableItemViewHolder) holder; final View containerView = holder2.getSwipeableContainerView(); if (containerView == null) { return; } final int reqBackgroundType; if (amount == 0.0f) { if (prevAmount == 0.0f) { reqBackgroundType = DRAWABLE_SWIPE_NEUTRAL_BACKGROUND; } else { reqBackgroundType = (prevAmount < 0) ? DRAWABLE_SWIPE_LEFT_BACKGROUND : DRAWABLE_SWIPE_RIGHT_BACKGROUND; } } else { reqBackgroundType = (amount < 0) ? DRAWABLE_SWIPE_LEFT_BACKGROUND : DRAWABLE_SWIPE_RIGHT_BACKGROUND; } if (amount == 0.0f) { slideItem(holder, amount, shouldAnimate); mAdapter.setSwipeBackgroundDrawable(holder, itemPosition, reqBackgroundType); } else { float adjustedAmount = amount; adjustedAmount = Math.max(adjustedAmount, holder2.getMaxLeftSwipeAmount()); adjustedAmount = Math.min(adjustedAmount, holder2.getMaxRightSwipeAmount()); mAdapter.setSwipeBackgroundDrawable(holder, itemPosition, reqBackgroundType); slideItem(holder, adjustedAmount, shouldAnimate); } } /*package*/ void cancelPendingAnimations(RecyclerView.ViewHolder holder) { if (mItemSlideAnimator != null) { mItemSlideAnimator.endAnimation(holder); } } /*package*/ int getSwipeContainerViewTranslationX(RecyclerView.ViewHolder holder) { return mItemSlideAnimator.getSwipeContainerViewTranslationX(holder); } private static boolean supportsViewPropertyAnimator() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; } public void setAdditionItemAnimatorListener(ViewPropertyAnimatorListener listener) { if (mItemSlideAnimator != null) { mItemSlideAnimator.setAdditionAnimatorListener(listener); } } }