2024年Android最全Android侧滑删除-RecyclerView轻松实现高效的侧滑菜单,腾讯面试岗位

最后

在此为大家准备了四节优质的Android高级进阶视频:

架构师项目实战——全球首批Android开发者对Android架构的见解

附相关架构及资料

image.png

往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

        });

    }

}



/**

 * 关闭一个打开的Item

 *

 * @param viewHolder 要关闭的Item的ViewHolder

 */

private void closeOpenedPreItem(RecyclerView.ViewHolder viewHolder) {

    if (viewHolder == null) return;

    final View view = getItemFrontView(viewHolder);

    if (view == null) return;

    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(view, "translationX", view.getTranslationX(), 0f);

    objectAnimator.addListener(new AnimatorListenerAdapter() {

        @Override

        public void onAnimationStart(Animator animation) {

            super.onAnimationStart(animation);

            if (mPreOpened != null) {

                if (mPendingCleanup.remove(mPreOpened.itemView)) {

                    mCallback.clearView(mRecyclerView, mPreOpened);

                }

            }

            endRecoverAnimation(mPreOpened, true);

            mPreOpened = mSelected;

        }



        @Override

        public void onAnimationEnd(Animator animation) {

            super.onAnimationEnd(animation);

            mRecoverAnimations.clear();

        }

    });



    objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());

    objectAnimator.start();

}



/**

 * @param viewHolder

 * @return

 */

public View getItemFrontView(RecyclerView.ViewHolder viewHolder) {

    if (viewHolder == null) return null;

    if (viewHolder.itemView instanceof ViewGroup) {

        ViewGroup viewGroup = (ViewGroup) viewHolder.itemView;

        for (int i = 0; i < viewGroup.getChildCount(); i++) {

            View childAt = viewGroup.getChildAt(i);

            String tag = (String) childAt.getTag();

            /*

            这个标记必须再xml布局里面.

            <RelativeLayout

              android:id="@+id/slide_itemView"

              android:clipChildren="false"

              android:tag="slide_flag"

              android:layout_width="match_parent"

              android:layout_height="match_parent">

             */

            if ("slide_flag".equals(tag)) {

                return childAt;

            }

        }

        return viewHolder.itemView;

    } else {

        return viewHolder.itemView;

    }

}



private void setupCallbacks() {

    ViewConfiguration vc = ViewConfiguration.get(mRecyclerView.getContext());

    mSlop = vc.getScaledTouchSlop();

    mRecyclerView.addItemDecoration(this);

    mRecyclerView.addOnItemTouchListener(mOnItemTouchListener);

    mRecyclerView.addOnChildAttachStateChangeListener(this);

    initGestureDetector();

}



private void destroyCallbacks() {

    mRecyclerView.removeItemDecoration(this);

    mRecyclerView.removeOnItemTouchListener(mOnItemTouchListener);

    mRecyclerView.removeOnChildAttachStateChangeListener(this);

    // clean all attached

    final int recoverAnimSize = mRecoverAnimations.size();

    for (int i = recoverAnimSize - 1; i >= 0; i--) {

        final WItemTouchHelperPlus.RecoverAnimation recoverAnimation = mRecoverAnimations.get(0);

        mCallback.clearView(mRecyclerView, recoverAnimation.mViewHolder);

    }

    mRecoverAnimations.clear();

    mOverdrawChild = null;

    mOverdrawChildPosition = -1;

    releaseVelocityTracker();

}



private void initGestureDetector() {

    if (mGestureDetector != null) {

        return;

    }

    mGestureDetector = new GestureDetectorCompat(mRecyclerView.getContext(),

            new WItemTouchHelperPlus.WItemTouchHelperPlusGestureListener());

}



private void getSelectedDxDy(float[] outPosition) {

    if ((mSelectedFlags & (LEFT | RIGHT)) != 0) {

        outPosition[0] = mSelectedStartX + mDx - mSelected.itemView.getLeft();

    } else {

        outPosition[0] = mSelected.itemView.getTranslationX();

    }

    if ((mSelectedFlags & (UP | DOWN)) != 0) {

        outPosition[1] = mSelectedStartY + mDy - mSelected.itemView.getTop();

    } else {

        outPosition[1] = mSelected.itemView.getTranslationY();

    }

}



@Override

public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {

    float dx = 0, dy = 0;

    if (mSelected != null) {

        getSelectedDxDy(mTmpPosition);

        dx = mTmpPosition[0];

        dy = mTmpPosition[1];

    }

    mCallback.onDrawOver(c, parent, mSelected,

            mRecoverAnimations, mActionState, dx, dy);

}



@Override

public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {

    // we don't know if RV changed something so we should invalidate this index.

    mOverdrawChildPosition = -1;

    float dx = 0, dy = 0;

    if (mSelected != null) {

        getSelectedDxDy(mTmpPosition);

        dx = mTmpPosition[0];

        dy = mTmpPosition[1];

    }

    mCallback.onDraw(c, parent, mSelected,

            mRecoverAnimations, mActionState, dx, dy);

}



private float getSwipeWidth() {

    if (mSelected instanceof Extension) {

        return ((Extension) mSelected).getActionWidth();

    }

    return mRecyclerView.getWidth();

}



/**

 * Starts dragging or swiping the given View. Call with null if you want to clear it.

 *

 * @param selected    The ViewHolder to drag or swipe. Can be null if you want to cancel the

 *                    current action

 * @param actionState The type of action

 */

void select(RecyclerView.ViewHolder selected, int actionState) {

    if (selected == mSelected && actionState == mActionState) {

        return;

    }

    mDragScrollStartTimeInMs = Long.MIN_VALUE;

    final int prevActionState = mActionState;

    // prevent duplicate animations

    endRecoverAnimation(selected, true);

    mActionState = actionState;

    if (actionState == ACTION_STATE_DRAG) {

        // we remove after animation is complete. this means we only elevate the last drag

        // child but that should perform good enough as it is very hard to start dragging a

        // new child before the previous one settles.

        mOverdrawChild = selected.itemView;

        addChildDrawingOrderCallback();

    }

    int actionStateMask = (1 << (DIRECTION_FLAG_COUNT + DIRECTION_FLAG_COUNT * actionState))

            - 1;

    boolean preventLayout = false;



    if (mSelected != null) {

        final RecyclerView.ViewHolder prevSelected = mSelected;

        if (prevSelected.itemView.getParent() != null) {

            final int swipeDir = prevActionState == ACTION_STATE_DRAG ? 0

                    : swipeIfNecessary(prevSelected);

            releaseVelocityTracker();

            // find where we should animate to

            final float targetTranslateX, targetTranslateY;

            int animationType;

            switch (swipeDir) {

                case LEFT:

                case RIGHT:

                case START:

                case END:

                    targetTranslateY = 0;

                    float swipeWidth = getSwipeWidth();

                    targetTranslateX = Math.signum(mDx) * swipeWidth;

                    break;

                case UP:

                case DOWN:

                    targetTranslateX = 0;

                    targetTranslateY = Math.signum(mDy) * mRecyclerView.getHeight();

                    break;

                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;

            }

            getSelectedDxDy(mTmpPosition);

            final float currentTranslateX = mTmpPosition[0];

            final float currentTranslateY = mTmpPosition[1];

            final WItemTouchHelperPlus.RecoverAnimation rv = new WItemTouchHelperPlus.RecoverAnimation(prevSelected, animationType,

                    prevActionState, currentTranslateX, currentTranslateY,

                    targetTranslateX, targetTranslateY) {

                @Override

                public void onAnimationEnd(Animator animation) {

                    super.onAnimationEnd(animation);

                    if (this.mOverridden) {

                        return;

                    }

                    if (swipeDir <= 0) {

                        mPreOpened = null;

                        // 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);

                        mPreOpened = prevSelected;

                        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);

                        }

                    }

                    // removed from the list after it is drawn for the last time

                    if (mOverdrawChild == prevSelected.itemView) {

                        removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);

                    }

                }



                @Override

                public void onAnimationStart(Animator animation) {

                    super.onAnimationStart(animation);

                    Log.e("WANG", "WItemTouchHelperPlus.onAnimationStart    Recover   ");

                }

            };

            final long duration = mCallback.getAnimationDuration(mRecyclerView, animationType,

                    targetTranslateX - currentTranslateX, targetTranslateY - currentTranslateY);

            rv.setDuration(duration);

            mRecoverAnimations.add(rv);

            rv.start();

            preventLayout = true;

        } else {

            removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);

            mCallback.clearView(mRecyclerView, prevSelected);

        }

        mSelected = null;

    }

    if (selected != null) {

        mSelectedFlags =

                (mCallback.getAbsoluteMovementFlags(mRecyclerView, selected) & actionStateMask)

                        >> (mActionState * DIRECTION_FLAG_COUNT);

        mSelectedStartX = selected.itemView.getLeft();

        mSelectedStartY = selected.itemView.getTop();

        mSelected = selected;



        if (actionState == ACTION_STATE_DRAG) {

            mSelected.itemView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);

        }

    }

    final ViewParent rvParent = mRecyclerView.getParent();

    if (rvParent != null) {

        rvParent.requestDisallowInterceptTouchEvent(mSelected != null);

    }

    if (!preventLayout) {

        mRecyclerView.getLayoutManager().requestSimpleAnimationsInNextLayout();

    }

    mCallback.onSelectedChanged(mSelected, mActionState);

    mRecyclerView.invalidate();

}



void postDispatchSwipe(final WItemTouchHelperPlus.RecoverAnimation anim, final int swipeDir) {

    // wait until animations are complete.

    mRecyclerView.post(new Runnable() {

        @Override

        public void run() {

            if (mRecyclerView != null && mRecyclerView.isAttachedToWindow()

                    && !anim.mOverridden

                    && anim.mViewHolder.getAdapterPosition() != RecyclerView.NO_POSITION) {

                final RecyclerView.ItemAnimator animator = mRecyclerView.getItemAnimator();

                // if animator is running or we have other active recover animations, we try

                // not to call onSwiped because DefaultItemAnimator is not good at merging

                // animations. Instead, we wait and batch.

                if ((animator == null || !animator.isRunning(null))

                        && !hasRunningRecoverAnim()) {

                    mCallback.onSwiped(anim.mViewHolder, swipeDir);

                } else {

                    mRecyclerView.post(this);

                }

            }

        }

    });

}



boolean hasRunningRecoverAnim() {

    final int size = mRecoverAnimations.size();

    for (int i = 0; i < size; i++) {

        if (!mRecoverAnimations.get(i).mEnded) {

            return true;

        }

    }

    return false;

}



/**

 * If user drags the view to the edge, trigger a scroll if necessary.

 */

boolean scrollIfNecessary() {

    if (mSelected == null) {

        mDragScrollStartTimeInMs = Long.MIN_VALUE;

        return false;

    }

    final long now = System.currentTimeMillis();

    final long scrollDuration = mDragScrollStartTimeInMs

            == Long.MIN_VALUE ? 0 : now - mDragScrollStartTimeInMs;

    RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();

    if (mTmpRect == null) {

        mTmpRect = new Rect();

    }

    int scrollX = 0;

    int scrollY = 0;

    lm.calculateItemDecorationsForChild(mSelected.itemView, mTmpRect);

    if (lm.canScrollHorizontally()) {

        int curX = (int) (mSelectedStartX + mDx);

        final int leftDiff = curX - mTmpRect.left - mRecyclerView.getPaddingLeft();

        if (mDx < 0 && leftDiff < 0) {

            scrollX = leftDiff;

        } else if (mDx > 0) {

            final int rightDiff =

                    curX + mSelected.itemView.getWidth() + mTmpRect.right

                            - (mRecyclerView.getWidth() - mRecyclerView.getPaddingRight());

            if (rightDiff > 0) {

                scrollX = rightDiff;

            }

        }

    }

    if (lm.canScrollVertically()) {

        int curY = (int) (mSelectedStartY + mDy);

        final int topDiff = curY - mTmpRect.top - mRecyclerView.getPaddingTop();

        if (mDy < 0 && topDiff < 0) {

            scrollY = topDiff;

        } else if (mDy > 0) {

            final int bottomDiff = curY + mSelected.itemView.getHeight() + mTmpRect.bottom

                    - (mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom());

            if (bottomDiff > 0) {

                scrollY = bottomDiff;

            }

        }

    }

    if (scrollX != 0) {

        scrollX = mCallback.interpolateOutOfBoundsScroll(mRecyclerView,

                mSelected.itemView.getWidth(), scrollX,

                mRecyclerView.getWidth(), scrollDuration);

    }

    if (scrollY != 0) {

        scrollY = mCallback.interpolateOutOfBoundsScroll(mRecyclerView,

                mSelected.itemView.getHeight(), scrollY,

                mRecyclerView.getHeight(), scrollDuration);

    }

    if (scrollX != 0 || scrollY != 0) {

        if (mDragScrollStartTimeInMs == Long.MIN_VALUE) {

            mDragScrollStartTimeInMs = now;

        }

        mRecyclerView.scrollBy(scrollX, scrollY);

        return true;

    }

    mDragScrollStartTimeInMs = Long.MIN_VALUE;

    return false;

}



private List<RecyclerView.ViewHolder> findSwapTargets(RecyclerView.ViewHolder viewHolder) {

    if (mSwapTargets == null) {

        mSwapTargets = new ArrayList<RecyclerView.ViewHolder>();

        mDistances = new ArrayList<Integer>();

    } else {

        mSwapTargets.clear();

        mDistances.clear();

    }

    final int margin = mCallback.getBoundingBoxMargin();

    final int left = Math.round(mSelectedStartX + mDx) - margin;

    final int top = Math.round(mSelectedStartY + mDy) - margin;

    final int right = left + viewHolder.itemView.getWidth() + 2 * margin;

    final int bottom = top + viewHolder.itemView.getHeight() + 2 * margin;

    final int centerX = (left + right) / 2;

    final int centerY = (top + bottom) / 2;

    final RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();

    final int childCount = lm.getChildCount();

    for (int i = 0; i < childCount; i++) {

        View other = lm.getChildAt(i);

        if (other == viewHolder.itemView) {

            continue; //myself!

        }

        if (other.getBottom() < top || other.getTop() > bottom

                || other.getRight() < left || other.getLeft() > right) {

            continue;

        }

        final RecyclerView.ViewHolder otherVh = mRecyclerView.getChildViewHolder(other);

        if (mCallback.canDropOver(mRecyclerView, mSelected, otherVh)) {

            // find the index to add

            final int dx = Math.abs(centerX - (other.getLeft() + other.getRight()) / 2);

            final int dy = Math.abs(centerY - (other.getTop() + other.getBottom()) / 2);

            final int dist = dx * dx + dy * dy;



            int pos = 0;

            final int cnt = mSwapTargets.size();

            for (int j = 0; j < cnt; j++) {

                if (dist > mDistances.get(j)) {

                    pos++;

                } else {

                    break;

                }

            }

            mSwapTargets.add(pos, otherVh);

            mDistances.add(pos, dist);

        }

    }

    return mSwapTargets;

}



/**

 * Checks if we should swap w/ another view holder.

 */

void moveIfNecessary(RecyclerView.ViewHolder viewHolder) {

    if (mRecyclerView.isLayoutRequested()) {

        return;

    }

    if (mActionState != ACTION_STATE_DRAG) {

        return;

    }



    final float threshold = mCallback.getMoveThreshold(viewHolder);

    final int x = (int) (mSelectedStartX + mDx);

    final int y = (int) (mSelectedStartY + mDy);

    if (Math.abs(y - viewHolder.itemView.getTop()) < viewHolder.itemView.getHeight() * threshold

            && Math.abs(x - viewHolder.itemView.getLeft())

            < viewHolder.itemView.getWidth() * threshold) {

        return;

    }

    List<RecyclerView.ViewHolder> swapTargets = findSwapTargets(viewHolder);

    if (swapTargets.size() == 0) {

        return;

    }

    // may swap.

    RecyclerView.ViewHolder target = mCallback.chooseDropTarget(viewHolder, swapTargets, x, y);

    if (target == null) {

        mSwapTargets.clear();

        mDistances.clear();

        return;

    }

    final int toPosition = target.getAdapterPosition();

    final int fromPosition = viewHolder.getAdapterPosition();

    if (mCallback.onMove(mRecyclerView, viewHolder, target)) {

        // keep target visible

        mCallback.onMoved(mRecyclerView, viewHolder, fromPosition,

                target, toPosition, x, y);

    }

}



@Override

public void onChildViewAttachedToWindow(View view) {

}



@Override

public void onChildViewDetachedFromWindow(View view) {

    removeChildDrawingOrderCallbackIfNecessary(view);

    final RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(view);

    if (holder == null) {

        return;

    }

    if (mSelected != null && holder == mSelected) {

        select(null, ACTION_STATE_IDLE);

    } else {

        endRecoverAnimation(holder, false); // this may push it into pending cleanup list.

        if (mPendingCleanup.remove(holder.itemView)) {

            mCallback.clearView(mRecyclerView, holder);

        }

    }

}



/**

 * Returns the animation type or 0 if cannot be found.

 */

int endRecoverAnimation(RecyclerView.ViewHolder viewHolder, boolean override) {

    final int recoverAnimSize = mRecoverAnimations.size();

    for (int i = recoverAnimSize - 1; i >= 0; i--) {

        final WItemTouchHelperPlus.RecoverAnimation anim = mRecoverAnimations.get(i);

        if (anim.mViewHolder == viewHolder) {

            anim.mOverridden |= override;

            if (!anim.mEnded) {

                anim.cancel();

            }

            mRecoverAnimations.remove(i);

            return anim.mAnimationType;

        }

    }

    return 0;

}





@Override

public void getItemOffsets(Rect outRect, View view, RecyclerView parent,

                           RecyclerView.State state) {

    outRect.setEmpty();

}



void obtainVelocityTracker() {

    if (mVelocityTracker != null) {

        mVelocityTracker.recycle();

    }

    mVelocityTracker = VelocityTracker.obtain();

}



private void releaseVelocityTracker() {

    if (mVelocityTracker != null) {

        mVelocityTracker.recycle();

        mVelocityTracker = null;

    }

}



private RecyclerView.ViewHolder findSwipedView(MotionEvent motionEvent) {

    final RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();

    if (mActivePointerId == ACTIVE_POINTER_ID_NONE) {

        return null;

    }

    final int pointerIndex = motionEvent.findPointerIndex(mActivePointerId);

    final float dx = motionEvent.getX(pointerIndex) - mInitialTouchX;

    final float dy = motionEvent.getY(pointerIndex) - mInitialTouchY;

    final float absDx = Math.abs(dx);

    final float absDy = Math.abs(dy);



    if (absDx < mSlop && absDy < mSlop) {

        return null;

    }

    if (absDx > absDy && lm.canScrollHorizontally()) {

        return null;

    } else if (absDy > absDx && lm.canScrollVertically()) {

        return null;

    }

    View child = findChildView(motionEvent);

    if (child == null) {

        return null;

    }

    return mRecyclerView.getChildViewHolder(child);

}



/**

 * Checks whether we should select a View for swiping.

 */

boolean checkSelectForSwipe(int action, MotionEvent motionEvent, int pointerIndex) {

    if (mSelected != null || action != MotionEvent.ACTION_MOVE

            || mActionState == ACTION_STATE_DRAG || !mCallback.isItemViewSwipeEnabled()) {

        return false;

    }

    if (mRecyclerView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) {

        return false;

    }

    final RecyclerView.ViewHolder vh = findSwipedView(motionEvent);

    if (vh == null) {

        return false;

    }

    final int movementFlags = mCallback.getAbsoluteMovementFlags(mRecyclerView, vh);



    final int swipeFlags = (movementFlags & ACTION_MODE_SWIPE_MASK)

            >> (DIRECTION_FLAG_COUNT * ACTION_STATE_SWIPE);



    if (swipeFlags == 0) {

        return false;

    }



    // mDx and mDy are only set in allowed directions. We use custom x/y here instead of

    // updateDxDy to avoid swiping if user moves more in the other direction

    final float x = motionEvent.getX(pointerIndex);

    final float y = motionEvent.getY(pointerIndex);



    // Calculate the distance moved

    final float dx = x - mInitialTouchX;

    final float dy = y - mInitialTouchY;

    // swipe target is chose w/o applying flags so it does not really check if swiping in that

    // direction is allowed. This why here, we use mDx mDy to check slope value again.

    final float absDx = Math.abs(dx);

    final float absDy = Math.abs(dy);



    if (absDx < mSlop && absDy < mSlop) {

        return false;

    }

    if (absDx > absDy) {

        if (dx < 0 && (swipeFlags & LEFT) == 0) {

            return false;

        }

        if (dx > 0 && (swipeFlags & RIGHT) == 0) {

            return false;

        }

    } else {

        if (dy < 0 && (swipeFlags & UP) == 0) {

            return false;

        }

        if (dy > 0 && (swipeFlags & DOWN) == 0) {

            return false;

        }

    }

    mDx = mDy = 0f;

    mActivePointerId = motionEvent.getPointerId(0);

    select(vh, ACTION_STATE_SWIPE);

    return true;

}



View findChildView(MotionEvent event) {

    // first check elevated views, if none, then call RV

    final float x = event.getX();

    final float y = event.getY();

    if (mSelected != null) {

        final View selectedView = mSelected.itemView;

        if (hitTest(selectedView, x, y, mSelectedStartX + mDx, mSelectedStartY + mDy, mSelected)) {

            return selectedView;

        }

    }

    for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {

        final WItemTouchHelperPlus.RecoverAnimation anim = mRecoverAnimations.get(i);

        final View view = anim.mViewHolder.itemView;

        boolean hitTest = hitTest(view, x, y, anim.mX, anim.mY, anim.mViewHolder);

        if (hitTest) {

            return view;

        }

    }

    View childViewUnder = mRecyclerView.findChildViewUnder(x, y);

    return childViewUnder;

}



/**

 * Starts dragging the provided ViewHolder. By default, WItemTouchHelperPlus starts a drag when a

 * View is long pressed. You can disable that behavior by overriding

 * {@link WItemTouchHelperPlus.Callback#isLongPressDragEnabled()}.

 * <p>

 * For this method to work:

 * <ul>

 * <li>The provided ViewHolder must be a child of the RecyclerView to which this

 * WItemTouchHelperPlus

 * is attached.</li>

 * <li>{@link WItemTouchHelperPlus.Callback} must have dragging enabled.</li>

 * <li>There must be a previous touch event that was reported to the WItemTouchHelperPlus

 * through RecyclerView's ItemTouchListener mechanism. As long as no other ItemTouchListener

 * grabs previous events, this should work as expected.</li>

 * </ul>

 * <p>

 * For example, if you would like to let your user to be able to drag an Item by touching one

 * of its descendants, you may implement it as follows:

 * <pre>

 *     viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {

 *         public boolean onTouch(View v, MotionEvent event) {

 *             if (MotionEvent.getActionMasked(event) == MotionEvent.ACTION_DOWN) {

 *                 mWItemTouchHelperPlus.startDrag(viewHolder);

 *             }

 *             return false;

 *         }

 *     });

 * </pre>

 * <p>

 *

 * @param viewHolder The ViewHolder to start dragging. It must be a direct child of

 *                   RecyclerView.

 * @see WItemTouchHelperPlus.Callback#isItemViewSwipeEnabled()

 */

public void startDrag(RecyclerView.ViewHolder viewHolder) {

    if (!mCallback.hasDragFlag(mRecyclerView, viewHolder)) {

        Log.e(TAG, "Start drag has been called but dragging is not enabled");

        return;

    }

    if (viewHolder.itemView.getParent() != mRecyclerView) {

        Log.e(TAG, "Start drag has been called with a view holder which is not a child of "

                + "the RecyclerView which is controlled by this WItemTouchHelperPlus.");

        return;

    }

    obtainVelocityTracker();

    mDx = mDy = 0f;

    select(viewHolder, ACTION_STATE_DRAG);

}



/**

 * Starts swiping the provided ViewHolder. By default, WItemTouchHelperPlus starts swiping a View

 * when user swipes their finger (or mouse pointer) over the View. You can disable this

 * behavior

 * by overriding {@link WItemTouchHelperPlus.Callback}

 * <p>

 * For this method to work:

 * <ul>

 * <li>The provided ViewHolder must be a child of the RecyclerView to which this

 * WItemTouchHelperPlus is attached.</li>

 * <li>{@link WItemTouchHelperPlus.Callback} must have swiping enabled.</li>

 * <li>There must be a previous touch event that was reported to the WItemTouchHelperPlus

 * through RecyclerView's ItemTouchListener mechanism. As long as no other ItemTouchListener

 * grabs previous events, this should work as expected.</li>

 * </ul>

 * <p>

 * For example, if you would like to let your user to be able to swipe an Item by touching one

 * of its descendants, you may implement it as follows:

 * <pre>

 *     viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {

 *         public boolean onTouch(View v, MotionEvent event) {

 *             if (MotionEvent.getActionMasked(event) == MotionEvent.ACTION_DOWN) {

 *                 mWItemTouchHelperPlus.startSwipe(viewHolder);

 *             }

 *             return false;

 *         }

 *     });

 * </pre>

 *

 * @param viewHolder The ViewHolder to start swiping. It must be a direct child of

 *                   RecyclerView.

 */

public void startSwipe(RecyclerView.ViewHolder viewHolder) {

    if (!mCallback.hasSwipeFlag(mRecyclerView, viewHolder)) {

        Log.e(TAG, "Start swipe has been called but swiping is not enabled");

        return;

    }

    if (viewHolder.itemView.getParent() != mRecyclerView) {

        Log.e(TAG, "Start swipe has been called with a view holder which is not a child of "

                + "the RecyclerView controlled by this WItemTouchHelperPlus.");

        return;

    }

    obtainVelocityTracker();

    mDx = mDy = 0f;

    select(viewHolder, ACTION_STATE_SWIPE);

}



WItemTouchHelperPlus.RecoverAnimation findAnimation(MotionEvent event) {

    if (mRecoverAnimations.isEmpty()) {

        return null;

    }

    View target = findChildView(event);

    for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {

        final WItemTouchHelperPlus.RecoverAnimation anim = mRecoverAnimations.get(i);

        if (anim.mViewHolder.itemView == target) {

            return anim;

        }

    }

    return null;

}



void updateDxDy(MotionEvent ev, int directionFlags, int pointerIndex) {

    final float x = ev.getX(pointerIndex);

    final float y = ev.getY(pointerIndex);



    // Calculate the distance moved

    mDx = x - mInitialTouchX;

    mDy = y - mInitialTouchY;

    if ((directionFlags & LEFT) == 0) {

        mDx = Math.max(0, mDx);

    }

    if ((directionFlags & RIGHT) == 0) {

        mDx = Math.min(0, mDx);

    }

    if ((directionFlags & UP) == 0) {

        mDy = Math.max(0, mDy);

    }

    if ((directionFlags & DOWN) == 0) {

        mDy = Math.min(0, mDy);

    }

}



private int swipeIfNecessary(RecyclerView.ViewHolder viewHolder) {

    if (mActionState == ACTION_STATE_DRAG) {

        return 0;

    }

    final int originalMovementFlags = mCallback.getMovementFlags(mRecyclerView, viewHolder);

    final int absoluteMovementFlags = mCallback.convertToAbsoluteDirection(

            originalMovementFlags,

            ViewCompat.getLayoutDirection(mRecyclerView));

    final int flags = (absoluteMovementFlags

            & ACTION_MODE_SWIPE_MASK) >> (ACTION_STATE_SWIPE * DIRECTION_FLAG_COUNT);

    if (flags == 0) {

        return 0;

    }

    final int originalFlags = (originalMovementFlags

            & ACTION_MODE_SWIPE_MASK) >> (ACTION_STATE_SWIPE * DIRECTION_FLAG_COUNT);

    int swipeDir;

    if (Math.abs(mDx) > Math.abs(mDy)) {

        if ((swipeDir = checkHorizontalSwipe(viewHolder, flags)) > 0) {

            // if swipe dir is not in original flags, it should be the relative direction

            if ((originalFlags & swipeDir) == 0) {

                // convert to relative

                return WItemTouchHelperPlus.Callback.convertToRelativeDirection(swipeDir,

                        ViewCompat.getLayoutDirection(mRecyclerView));

            }

            return swipeDir;

        }

        if ((swipeDir = checkVerticalSwipe(viewHolder, flags)) > 0) {

            return swipeDir;

        }

    } else {

        if ((swipeDir = checkVerticalSwipe(viewHolder, flags)) > 0) {

            return swipeDir;

        }

        if ((swipeDir = checkHorizontalSwipe(viewHolder, flags)) > 0) {

            // if swipe dir is not in original flags, it should be the relative direction

            if ((originalFlags & swipeDir) == 0) {

                // convert to relative

                return WItemTouchHelperPlus.Callback.convertToRelativeDirection(swipeDir,

                        ViewCompat.getLayoutDirection(mRecyclerView));

            }

            return swipeDir;

        }

    }

    return 0;

}



private int checkHorizontalSwipe(RecyclerView.ViewHolder viewHolder, int flags) {

    if ((flags & (LEFT | RIGHT)) != 0) {

        final int dirFlag = mDx > 0 ? RIGHT : LEFT;

        if (mVelocityTracker != null && mActivePointerId > -1) {

            mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,

                    mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));

            final float xVelocity = mVelocityTracker.getXVelocity(mActivePointerId);

            final float yVelocity = mVelocityTracker.getYVelocity(mActivePointerId);

            final int velDirFlag = xVelocity > 0f ? RIGHT : LEFT;

            final float absXVelocity = Math.abs(xVelocity);

            if ((velDirFlag & flags) != 0 && dirFlag == velDirFlag

                    && absXVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity)

                    && absXVelocity > Math.abs(yVelocity)) {

                return velDirFlag;

            }

        }

        int width = mRecyclerView.getWidth();

        if (viewHolder instanceof Extension && mCallback.getItemSlideType().equals(SLIDE_ITEM_TYPE_ITEMVIEW)) {

            Extension extension = (Extension) viewHolder;

            width += (int) extension.getActionWidth();

        }

        final float threshold = width * mCallback

                .getSwipeThreshold(viewHolder);



        if ((flags & dirFlag) != 0 && Math.abs(mDx) > threshold) {

            return dirFlag;

        }

    }

    return 0;

}



private int checkVerticalSwipe(RecyclerView.ViewHolder viewHolder, int flags) {

    if ((flags & (UP | DOWN)) != 0) {

        final int dirFlag = mDy > 0 ? DOWN : UP;

        if (mVelocityTracker != null && mActivePointerId > -1) {

            mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,

                    mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));

            final float xVelocity = mVelocityTracker.getXVelocity(mActivePointerId);

            final float yVelocity = mVelocityTracker.getYVelocity(mActivePointerId);

            final int velDirFlag = yVelocity > 0f ? DOWN : UP;

            final float absYVelocity = Math.abs(yVelocity);

            if ((velDirFlag & flags) != 0 && velDirFlag == dirFlag

                    && absYVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity)

                    && absYVelocity > Math.abs(xVelocity)) {

                return velDirFlag;

            }

        }



        final float threshold = mRecyclerView.getHeight() * mCallback

                .getSwipeThreshold(viewHolder);

        if ((flags & dirFlag) != 0 && Math.abs(mDy) > threshold) {

            return dirFlag;

        }

    }

    return 0;

}



private void addChildDrawingOrderCallback() {

    if (Build.VERSION.SDK_INT >= 21) {

        return; // we use elevation on Lollipop

    }

    if (mChildDrawingOrderCallback == null) {

        mChildDrawingOrderCallback = new RecyclerView.ChildDrawingOrderCallback() {

            @Override

            public int onGetChildDrawingOrder(int childCount, int i) {

                if (mOverdrawChild == null) {

                    return i;

                }

                int childPosition = mOverdrawChildPosition;

                if (childPosition == -1) {

                    childPosition = mRecyclerView.indexOfChild(mOverdrawChild);

                    mOverdrawChildPosition = childPosition;

                }

                if (i == childCount - 1) {

                    return childPosition;

                }

                return i < childPosition ? i : i + 1;

            }

        };

    }

    mRecyclerView.setChildDrawingOrderCallback(mChildDrawingOrderCallback);

}



void removeChildDrawingOrderCallbackIfNecessary(View view) {

    if (view == mOverdrawChild) {

        mOverdrawChild = null;

        // only remove if we've added

        if (mChildDrawingOrderCallback != null) {

            mRecyclerView.setChildDrawingOrderCallback(null);

        }

    }

}



/**

 * An interface which can be implemented by LayoutManager for better integration with

 * {@link WItemTouchHelperPlus}.

 */

public interface ViewDropHandler {



    /**

     * Called by the {@link WItemTouchHelperPlus} after a View is dropped over another View.

     * <p>

     * A LayoutManager should implement this interface to get ready for the upcoming move

     * operation.

     * <p>

     * For example, LinearLayoutManager sets up a "scrollToPositionWithOffset" calls so that

     * the View under drag will be used as an anchor View while calculating the next layout,

     * making layout stay consistent.

     *

     * @param view   The View which is being dragged. It is very likely that user is still

     *               dragging this View so there might be other

     *               {@link #prepareForDrop(View, View, int, int)} after this one.

     * @param target The target view which is being dropped on.

     * @param x      The <code>left</code> offset of the View that is being dragged. This value

     *               includes the movement caused by the user.

     * @param y      The <code>top</code> offset of the View that is being dragged. This value

     *               includes the movement caused by the user.

     */

    void prepareForDrop(View view, View target, int x, int y);

}



/**

 * This class is the contract between WItemTouchHelperPlus and your application. It lets you control

 * which touch behaviors are enabled per each ViewHolder and also receive callbacks when user

 * performs these actions.

 * <p>

 * To control which actions user can take on each view, you should override

 * {@link #getMovementFlags(RecyclerView, RecyclerView.ViewHolder)} and return appropriate set

 * of direction flags. ({@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link #END},

 * {@link #UP}, {@link #DOWN}). You can use

 * {@link #makeMovementFlags(int, int)} to easily construct it. Alternatively, you can use

 * {@link WItemTouchHelperPlus.SimpleCallback}.

 * <p>

 * If user drags an item, WItemTouchHelperPlus will call

 * {@link WItemTouchHelperPlus.Callback#onMove(RecyclerView, RecyclerView.ViewHolder, RecyclerView.ViewHolder)

 * onMove(recyclerView, dragged, target)}.

 * Upon receiving this callback, you should move the item from the old position

 * ({@code dragged.getAdapterPosition()}) to new position ({@code target.getAdapterPosition()})

 * in your adapter and also call {@link RecyclerView.Adapter#notifyItemMoved(int, int)}.

 * To control where a View can be dropped, you can override

 * {@link #canDropOver(RecyclerView, RecyclerView.ViewHolder, RecyclerView.ViewHolder)}. When a

 * dragging View overlaps multiple other views, Callback chooses the closest View with which

 * dragged View might have changed positions. Although this approach works for many use cases,

 * if you have a custom LayoutManager, you can override

 * {@link #chooseDropTarget(RecyclerView.ViewHolder, java.util.List, int, int)} to select a

 * custom drop target.

 * <p>

 * When a View is swiped, WItemTouchHelperPlus animates it until it goes out of bounds, then calls

 * {@link #onSwiped(RecyclerView.ViewHolder, int)}. At this point, you should update your

 * adapter (e.g. remove the item) and call related Adapter#notify event.

 */

@SuppressWarnings("UnusedParameters")

public abstract static class Callback {



    public static final int DEFAULT_DRAG_ANIMATION_DURATION = 200;



    public static final int DEFAULT_SWIPE_ANIMATION_DURATION = 250;



    static final int RELATIVE_DIR_FLAGS = START | END

            | ((START | END) << DIRECTION_FLAG_COUNT)

            | ((START | END) << (2 * DIRECTION_FLAG_COUNT));



    private static final ItemTouchUIUtil sUICallback;



    private static final int ABS_HORIZONTAL_DIR_FLAGS = LEFT | RIGHT

            | ((LEFT | RIGHT) << DIRECTION_FLAG_COUNT)

            | ((LEFT | RIGHT) << (2 * DIRECTION_FLAG_COUNT));



    private static final Interpolator sDragScrollInterpolator = new Interpolator() {

        @Override

        public float getInterpolation(float t) {

            return t * t * t * t * t;

        }

    };



    private static final Interpolator sDragViewScrollCapInterpolator = new Interpolator() {

        @Override

        public float getInterpolation(float t) {

            t -= 1.0f;

            return t * t * t * t * t + 1.0f;

        }

    };



    /**

     * Drag scroll speed keeps accelerating until this many milliseconds before being capped.

     */

    private static final long DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS = 2000;



    private int mCachedMaxScrollSpeed = -1;



    static {

        if (Build.VERSION.SDK_INT >= 21) {

            sUICallback = new ItemTouchUIUtilImpl.Lollipop();

        } else {

            sUICallback = new ItemTouchUIUtilImpl.Honeycomb();

        }

    }



    /**

     * Returns the {@link ItemTouchUIUtil} that is used by the {@link WItemTouchHelperPlus.Callback} class for

     * visual

     * changes on Views in response to user interactions. {@link ItemTouchUIUtil} has different

     * implementations for different platform versions.

     * <p>

     * By default, {@link WItemTouchHelperPlus.Callback} applies these changes on

     * {@link RecyclerView.ViewHolder#itemView}.

     * <p>

     * For example, if you have a use case where you only want the text to move when user

     * swipes over the view, you can do the following:

     * <pre>

     *     public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder){

     *         getDefaultUIUtil().clearView(((ItemTouchViewHolder) viewHolder).textView);

     *     }

     *     public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {

     *         if (viewHolder != null){

     *             getDefaultUIUtil().onSelected(((ItemTouchViewHolder) viewHolder).textView);

     *         }

     *     }

     *     public void onChildDraw(Canvas c, RecyclerView recyclerView,

     *             RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState,

     *             boolean isCurrentlyActive) {

     *         getDefaultUIUtil().onDraw(c, recyclerView,

     *                 ((ItemTouchViewHolder) viewHolder).textView, dX, dY,

     *                 actionState, isCurrentlyActive);

     *         return true;

     *     }

     *     public void onChildDrawOver(Canvas c, RecyclerView recyclerView,

     *             RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState,

     *             boolean isCurrentlyActive) {

     *         getDefaultUIUtil().onDrawOver(c, recyclerView,

     *                 ((ItemTouchViewHolder) viewHolder).textView, dX, dY,

     *                 actionState, isCurrentlyActive);

     *         return true;

     *     }

     * </pre>

     *

     * @return The {@link ItemTouchUIUtil} instance that is used by the {@link WItemTouchHelperPlus.Callback}

     */

    public static ItemTouchUIUtil getDefaultUIUtil() {

        return sUICallback;

    }



    /**

     * Replaces a movement direction with its relative version by taking layout direction into

     * account.

     *

     * @param flags           The flag value that include any number of movement flags.

     * @param layoutDirection The layout direction of the View. Can be obtained from

     *                        {@link ViewCompat#getLayoutDirection(android.view.View)}.

     * @return Updated flags which uses relative flags ({@link #START}, {@link #END}) instead

     * of {@link #LEFT}, {@link #RIGHT}.

     * @see #convertToAbsoluteDirection(int, int)

     */

    public static int convertToRelativeDirection(int flags, int layoutDirection) {

        int masked = flags & ABS_HORIZONTAL_DIR_FLAGS;

        if (masked == 0) {

            return flags; // does not have any abs flags, good.

        }

        flags &= ~masked; //remove left / right.

        if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {

            // no change. just OR with 2 bits shifted mask and return

            flags |= masked << 2; // START is 2 bits after LEFT, END is 2 bits after RIGHT.

            return flags;

        } else {

            // add RIGHT flag as START

            flags |= ((masked << 1) & ~ABS_HORIZONTAL_DIR_FLAGS);

            // first clean RIGHT bit then add LEFT flag as END

            flags |= ((masked << 1) & ABS_HORIZONTAL_DIR_FLAGS) << 2;

        }

        return flags;

    }



    /**

     * Convenience method to create movement flags.

     * <p>

     * For instance, if you want to let your items be drag & dropped vertically and swiped

     * left to be dismissed, you can call this method with:

     * <code>makeMovementFlags(UP | DOWN, LEFT);</code>

     *

     * @param dragFlags  The directions in which the item can be dragged.

     * @param swipeFlags The directions in which the item can be swiped.

     * @return Returns an integer composed of the given drag and swipe flags.

     */

    public static int makeMovementFlags(int dragFlags, int swipeFlags) {

        return makeFlag(ACTION_STATE_IDLE, swipeFlags | dragFlags)

                | makeFlag(ACTION_STATE_SWIPE, swipeFlags)

                | makeFlag(ACTION_STATE_DRAG, dragFlags);

    }



    /**

     * Shifts the given direction flags to the offset of the given action state.

     *

     * @param actionState The action state you want to get flags in. Should be one of

     *                    {@link #ACTION_STATE_IDLE}, {@link #ACTION_STATE_SWIPE} or

     *                    {@link #ACTION_STATE_DRAG}.

     * @param directions  The direction flags. Can be composed from {@link #UP}, {@link #DOWN},

     *                    {@link #RIGHT}, {@link #LEFT} {@link #START} and {@link #END}.

     * @return And integer that represents the given directions in the provided actionState.

     */

    public static int makeFlag(int actionState, int directions) {

        return directions << (actionState * DIRECTION_FLAG_COUNT);

    }



    abstract int getSlideViewWidth();



    /**

     * Should return a composite flag which defines the enabled move directions in each state

     * (idle, swiping, dragging).

     * <p>

     * Instead of composing this flag manually, you can use {@link #makeMovementFlags(int,

     * int)}

     * or {@link #makeFlag(int, int)}.

     * <p>

     * This flag is composed of 3 sets of 8 bits, where first 8 bits are for IDLE state, next

     * 8 bits are for SWIPE state and third 8 bits are for DRAG state.

     * Each 8 bit sections can be constructed by simply OR'ing direction flags defined in

     * {@link WItemTouchHelperPlus}.

     * <p>

     * For example, if you want it to allow swiping LEFT and RIGHT but only allow starting to

     * swipe by swiping RIGHT, you can return:

     * <pre>

     *      makeFlag(ACTION_STATE_IDLE, RIGHT) | makeFlag(ACTION_STATE_SWIPE, LEFT | RIGHT);

     * </pre>

     * This means, allow right movement while IDLE and allow right and left movement while

     * swiping.

     *

     * @param recyclerView The RecyclerView to which WItemTouchHelperPlus is attached.

     * @param viewHolder   The ViewHolder for which the movement information is necessary.

     * @return flags specifying which movements are allowed on this ViewHolder.

     * @see #makeMovementFlags(int, int)

     * @see #makeFlag(int, int)

     */

    public abstract int getMovementFlags(RecyclerView recyclerView,

                                         RecyclerView.ViewHolder viewHolder);



    public abstract String getItemSlideType();



    /**

     * Converts a given set of flags to absolution direction which means {@link #START} and

     * {@link #END} are replaced with {@link #LEFT} and {@link #RIGHT} depending on the layout

     * direction.

     *

     * @param flags           The flag value that include any number of movement flags.

     * @param layoutDirection The layout direction of the RecyclerView.

     * @return Updated flags which includes only absolute direction values.

     */

    public int convertToAbsoluteDirection(int flags, int layoutDirection) {

        int masked = flags & RELATIVE_DIR_FLAGS;

        if (masked == 0) {

            return flags; // does not have any relative flags, good.

        }

        flags &= ~masked; //remove start / end

        if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {

            // no change. just OR with 2 bits shifted mask and return

            flags |= masked >> 2; // START is 2 bits after LEFT, END is 2 bits after RIGHT.

            return flags;

        } else {

            // add START flag as RIGHT

            flags |= ((masked >> 1) & ~RELATIVE_DIR_FLAGS);

            // first clean start bit then add END flag as LEFT

            flags |= ((masked >> 1) & RELATIVE_DIR_FLAGS) >> 2;

        }

        return flags;

    }



    final int getAbsoluteMovementFlags(RecyclerView recyclerView,

                                       RecyclerView.ViewHolder viewHolder) {

        final int flags = getMovementFlags(recyclerView, viewHolder);

        return convertToAbsoluteDirection(flags, ViewCompat.getLayoutDirection(recyclerView));

    }



    boolean hasDragFlag(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {

        final int flags = getAbsoluteMovementFlags(recyclerView, viewHolder);

        return (flags & ACTION_MODE_DRAG_MASK) != 0;

    }



    boolean hasSwipeFlag(RecyclerView recyclerView,

                         RecyclerView.ViewHolder viewHolder) {

        final int flags = getAbsoluteMovementFlags(recyclerView, viewHolder);

        return (flags & ACTION_MODE_SWIPE_MASK) != 0;

    }



    /**

     * Return true if the current ViewHolder can be dropped over the the target ViewHolder.

     * <p>

     * This method is used when selecting drop target for the dragged View. After Views are

     * eliminated either via bounds check or via this method, resulting set of views will be

     * passed to {@link #chooseDropTarget(RecyclerView.ViewHolder, java.util.List, int, int)}.

     * <p>

     * Default implementation returns true.

     *

     * @param recyclerView The RecyclerView to which WItemTouchHelperPlus is attached to.

     * @param current      The ViewHolder that user is dragging.

     * @param target       The ViewHolder which is below the dragged ViewHolder.

     * @return True if the dragged ViewHolder can be replaced with the target ViewHolder, false

     * otherwise.

     */

    public boolean canDropOver(RecyclerView recyclerView, RecyclerView.ViewHolder current,

                               RecyclerView.ViewHolder target) {

        return true;

    }



    /**

     * Called when WItemTouchHelperPlus wants to move the dragged item from its old position to

     * the new position.

     * <p>

     * If this method returns true, WItemTouchHelperPlus assumes {@code viewHolder} has been moved

     * to the adapter position of {@code target} ViewHolder

     * ({@link RecyclerView.ViewHolder#getAdapterPosition()

     * ViewHolder#getAdapterPosition()}).

     * <p>

     * If you don't support drag & drop, this method will never be called.

     *

     * @param recyclerView The RecyclerView to which WItemTouchHelperPlus is attached to.

     * @param viewHolder   The ViewHolder which is being dragged by the user.

     * @param target       The ViewHolder over which the currently active item is being

     *                     dragged.

     * @return True if the {@code viewHolder} has been moved to the adapter position of

     * {@code target}.

     * @see #onMoved(RecyclerView, RecyclerView.ViewHolder, int, RecyclerView.ViewHolder, int, int, int)

     */

    public abstract boolean onMove(RecyclerView recyclerView,

                                   RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target);



    /**

     * Returns whether WItemTouchHelperPlus should start a drag and drop operation if an item is

     * long pressed.

     * <p>

     * Default value returns true but you may want to disable this if you want to start

     * dragging on a custom view touch using {@link #startDrag(RecyclerView.ViewHolder)}.

     *

     * @return True if WItemTouchHelperPlus should start dragging an item when it is long pressed,

     * false otherwise. Default value is <code>true</code>.

     * @see #startDrag(RecyclerView.ViewHolder)

     */

    public boolean isLongPressDragEnabled() {

        return true;

    }



    /**

     * Returns whether WItemTouchHelperPlus should start a swipe operation if a pointer is swiped

     * over the View.

     * <p>

     * Default value returns true but you may want to disable this if you want to start

     * swiping on a custom view touch using {@link #startSwipe(RecyclerView.ViewHolder)}.

     *

     * @return True if WItemTouchHelperPlus should start swiping an item when user swipes a pointer

     * over the View, false otherwise. Default value is <code>true</code>.

     * @see #startSwipe(RecyclerView.ViewHolder)

     */

    public boolean isItemViewSwipeEnabled() {

        return true;

    }



    /**

     * When finding views under a dragged view, by default, WItemTouchHelperPlus searches for views

     * that overlap with the dragged View. By overriding this method, you can extend or shrink

     * the search box.

     *

     * @return The extra margin to be added to the hit box of the dragged View.

     */

    public int getBoundingBoxMargin() {

        return 0;

    }



    /**

     * Returns the fraction that the user should move the View to be considered as swiped.

     * The fraction is calculated with respect to RecyclerView's bounds.

     * <p>

     * Default value is .5f, which means, to swipe a View, user must move the View at least

     * half of RecyclerView's width or height, depending on the swipe direction.

     *

     * @param viewHolder The ViewHolder that is being dragged.

     * @return A float value that denotes the fraction of the View size. Default value

     * is .5f .

     */

    public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {

        return .5f;

    }



    /**

     * Returns the fraction that the user should move the View to be considered as it is

     * dragged. After a view is moved this amount, WItemTouchHelperPlus starts checking for Views

     * below it for a possible drop.

     *

     * @param viewHolder The ViewHolder that is being dragged.

     * @return A float value that denotes the fraction of the View size. Default value is

     * .5f .

     */

    public float getMoveThreshold(RecyclerView.ViewHolder viewHolder) {

        return .5f;

    }



    /**

     * Defines the minimum velocity which will be considered as a swipe action by the user.

     * <p>

     * You can increase this value to make it harder to swipe or decrease it to make it easier.

     * Keep in mind that WItemTouchHelperPlus also checks the perpendicular velocity and makes sure

     * current direction velocity is larger then the perpendicular one. Otherwise, user's

     * movement is ambiguous. You can change the threshold by overriding

     * {@link #getSwipeVelocityThreshold(float)}.

     * <p>

     * The velocity is calculated in pixels per second.

     * <p>

     * The default framework value is passed as a parameter so that you can modify it with a

     * multiplier.

     *

     * @param defaultValue The default value (in pixels per second) used by the

     *                     WItemTouchHelperPlus.

     * @return The minimum swipe velocity. The default implementation returns the

     * <code>defaultValue</code> parameter.

     * @see #getSwipeVelocityThreshold(float)

     * @see #getSwipeThreshold(RecyclerView.ViewHolder)

     */

    public float getSwipeEscapeVelocity(float defaultValue) {

        return defaultValue;

    }



    /**

     * Defines the maximum velocity WItemTouchHelperPlus will ever calculate for pointer movements.

     * <p>

     * To consider a movement as swipe, WItemTouchHelperPlus requires it to be larger than the

     * perpendicular movement. If both directions reach to the max threshold, none of them will

     * be considered as a swipe because it is usually an indication that user rather tried to

     * scroll then swipe.

     * <p>

     * The velocity is calculated in pixels per second.

     * <p>

     * You can customize this behavior by changing this method. If you increase the value, it

     * will be easier for the user to swipe diagonally and if you decrease the value, user will

     * need to make a rather straight finger movement to trigger a swipe.

     *

     * @param defaultValue The default value(in pixels per second) used by the WItemTouchHelperPlus.

     * @return The velocity cap for pointer movements. The default implementation returns the

     * <code>defaultValue</code> parameter.

     * @see #getSwipeEscapeVelocity(float)

     */

    public float getSwipeVelocityThreshold(float defaultValue) {

        return defaultValue;

    }



    /**

     * Called by WItemTouchHelperPlus to select a drop target from the list of ViewHolders that

     * are under the dragged View.

     * <p>

     * Default implementation filters the View with which dragged item have changed position

     * in the drag direction. For instance, if the view is dragged UP, it compares the

     * <code>view.getTop()</code> of the two views before and after drag started. If that value

     * is different, the target view passes the filter.

     * <p>

     * Among these Views which pass the test, the one closest to the dragged view is chosen.

     * <p>

     * This method is called on the main thread every time user moves the View. If you want to

     * override it, make sure it does not do any expensive operations.

     *

     * @param selected    The ViewHolder being dragged by the user.

     * @param dropTargets The list of ViewHolder that are under the dragged View and

     *                    candidate as a drop.

     * @param curX        The updated left value of the dragged View after drag translations

     *                    are applied. This value does not include margins added by

     *                    {@link RecyclerView.ItemDecoration}s.

     * @param curY        The updated top value of the dragged View after drag translations

     *                    are applied. This value does not include margins added by

     *                    {@link RecyclerView.ItemDecoration}s.

     * @return A ViewHolder to whose position the dragged ViewHolder should be

     * moved to.

     */

    public RecyclerView.ViewHolder chooseDropTarget(RecyclerView.ViewHolder selected,

                                                    List<RecyclerView.ViewHolder> dropTargets, int curX, int curY) {

        int right = curX + selected.itemView.getWidth();

        int bottom = curY + selected.itemView.getHeight();

        RecyclerView.ViewHolder winner = null;

        int winnerScore = -1;

        final int dx = curX - selected.itemView.getLeft();

        final int dy = curY - selected.itemView.getTop();

        final int targetsSize = dropTargets.size();

        for (int i = 0; i < targetsSize; i++) {

            final RecyclerView.ViewHolder target = dropTargets.get(i);

            if (dx > 0) {

                int diff = target.itemView.getRight() - right;

                if (diff < 0 && target.itemView.getRight() > selected.itemView.getRight()) {

                    final int score = Math.abs(diff);

                    if (score > winnerScore) {

                        winnerScore = score;

                        winner = target;

                    }

                }

            }

            if (dx < 0) {

                int diff = target.itemView.getLeft() - curX;

                if (diff > 0 && target.itemView.getLeft() < selected.itemView.getLeft()) {

                    final int score = Math.abs(diff);

                    if (score > winnerScore) {

                        winnerScore = score;

                        winner = target;

                    }

                }

            }

            if (dy < 0) {

                int diff = target.itemView.getTop() - curY;

                if (diff > 0 && target.itemView.getTop() < selected.itemView.getTop()) {

                    final int score = Math.abs(diff);

                    if (score > winnerScore) {

                        winnerScore = score;

                        winner = target;

                    }

                }

            }



            if (dy > 0) {

                int diff = target.itemView.getBottom() - bottom;

                if (diff < 0 && target.itemView.getBottom() > selected.itemView.getBottom()) {

                    final int score = Math.abs(diff);

                    if (score > winnerScore) {

                        winnerScore = score;

                        winner = target;

                    }

                }

            }

        }

        return winner;

    }



    /**

     * Called when a ViewHolder is swiped by the user.

     * <p>

     * If you are returning relative directions ({@link #START} , {@link #END}) from the

     * {@link #getMovementFlags(RecyclerView, RecyclerView.ViewHolder)} method, this method

     * will also use relative directions. Otherwise, it will use absolute directions.

     * <p>

     * If you don't support swiping, this method will never be called.

     * <p>

     * WItemTouchHelperPlus will keep a reference to the View until it is detached from

     * RecyclerView.

     * As soon as it is detached, WItemTouchHelperPlus will call

     * {@link #clearView(RecyclerView, RecyclerView.ViewHolder)}.

     *

     * @param viewHolder The ViewHolder which has been swiped by the user.

     * @param direction  The direction to which the ViewHolder is swiped. It is one of

     *                   {@link #UP}, {@link #DOWN},

     *                   {@link #LEFT} or {@link #RIGHT}. If your

     *                   {@link #getMovementFlags(RecyclerView, RecyclerView.ViewHolder)}

     *                   method

     *                   returned relative flags instead of {@link #LEFT} / {@link #RIGHT};

     *                   `direction` will be relative as well. ({@link #START} or {@link

     *                   #END}).

     */

    public abstract void onSwiped(RecyclerView.ViewHolder viewHolder, int direction);



    /**

     * Called when the ViewHolder swiped or dragged by the WItemTouchHelperPlus is changed.

     * <p/>

     * If you override this method, you should call super.

     *

     * @param viewHolder  The new ViewHolder that is being swiped or dragged. Might be null if

     *                    it is cleared.

     * @param actionState One of {@link WItemTouchHelperPlus#ACTION_STATE_IDLE},

     *                    {@link WItemTouchHelperPlus#ACTION_STATE_SWIPE} or

     *                    {@link WItemTouchHelperPlus#ACTION_STATE_DRAG}.

     * @see #clearView(RecyclerView, RecyclerView.ViewHolder)

     */

    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {

        if (viewHolder != null) {

            sUICallback.onSelected(viewHolder.itemView);

        }

    }



    private int getMaxDragScroll(RecyclerView recyclerView) {

        if (mCachedMaxScrollSpeed == -1) {

            mCachedMaxScrollSpeed = recyclerView.getResources().getDimensionPixelSize(

                    android.support.v7.recyclerview.R.dimen.item_touch_helper_max_drag_scroll_per_frame);

        }

        return mCachedMaxScrollSpeed;

    }



    /**

     * Called when {@link #onMove(RecyclerView, RecyclerView.ViewHolder, RecyclerView.ViewHolder)} returns true.

     * <p>

     * WItemTouchHelperPlus does not create an extra Bitmap or View while dragging, instead, it

     * modifies the existing View. Because of this reason, it is important that the View is

     * still part of the layout after it is moved. This may not work as intended when swapped

     * Views are close to RecyclerView bounds or there are gaps between them (e.g. other Views

     * which were not eligible for dropping over).

     * <p>

     * This method is responsible to give necessary hint to the LayoutManager so that it will

     * keep the View in visible area. For example, for LinearLayoutManager, this is as simple

     * as calling {@link LinearLayoutManager#scrollToPositionWithOffset(int, int)}.

     * <p>

     * Default implementation calls {@link RecyclerView#scrollToPosition(int)} if the View's

     * new position is likely to be out of bounds.

     * <p>

     * It is important to ensure the ViewHolder will stay visible as otherwise, it might be

     * removed by the LayoutManager if the move causes the View to go out of bounds. In that

     * case, drag will end prematurely.

     *

     * @param recyclerView The RecyclerView controlled by the WItemTouchHelperPlus.

     * @param viewHolder   The ViewHolder under user's control.

     * @param fromPos      The previous adapter position of the dragged item (before it was

     *                     moved).

     * @param target       The ViewHolder on which the currently active item has been dropped.

     * @param toPos        The new adapter position of the dragged item.

     * @param x            The updated left value of the dragged View after drag translations

     *                     are applied. This value does not include margins added by

     *                     {@link RecyclerView.ItemDecoration}s.

     * @param y            The updated top value of the dragged View after drag translations

     *                     are applied. This value does not include margins added by

     *                     {@link RecyclerView.ItemDecoration}s.

     */

    public void onMoved(final RecyclerView recyclerView,

                        final RecyclerView.ViewHolder viewHolder, int fromPos, final RecyclerView.ViewHolder target, int toPos, int x,

                        int y) {

        final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();

        if (layoutManager instanceof WItemTouchHelperPlus.ViewDropHandler) {

            ((WItemTouchHelperPlus.ViewDropHandler) layoutManager).prepareForDrop(viewHolder.itemView,

                    target.itemView, x, y);

            return;

        }



        // if layout manager cannot handle it, do some guesswork

        if (layoutManager.canScrollHorizontally()) {

            final int minLeft = layoutManager.getDecoratedLeft(target.itemView);

            if (minLeft <= recyclerView.getPaddingLeft()) {

                recyclerView.scrollToPosition(toPos);

            }

            final int maxRight = layoutManager.getDecoratedRight(target.itemView);

            if (maxRight >= recyclerView.getWidth() - recyclerView.getPaddingRight()) {

                recyclerView.scrollToPosition(toPos);

            }

        }



        if (layoutManager.canScrollVertically()) {

            final int minTop = layoutManager.getDecoratedTop(target.itemView);

            if (minTop <= recyclerView.getPaddingTop()) {

                recyclerView.scrollToPosition(toPos);

            }

            final int maxBottom = layoutManager.getDecoratedBottom(target.itemView);

            if (maxBottom >= recyclerView.getHeight() - recyclerView.getPaddingBottom()) {

                recyclerView.scrollToPosition(toPos);

            }

        }

    }



    void onDraw(Canvas c, RecyclerView parent, RecyclerView.ViewHolder selected,

                List<WItemTouchHelperPlus.RecoverAnimation> recoverAnimationList,

                int actionState, float dX, float dY) {

        final int recoverAnimSize = recoverAnimationList.size();

        for (int i = 0; i < recoverAnimSize; i++) {

            final WItemTouchHelperPlus.RecoverAnimation anim = recoverAnimationList.get(i);

            anim.update();

            final int count = c.save();

            onChildDraw(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState,

                    false);

            c.restoreToCount(count);

        }

        if (selected != null) {

            final int count = c.save();

            onChildDraw(c, parent, selected, dX, dY, actionState, true);

            c.restoreToCount(count);

        }

    }



    void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.ViewHolder selected,

                    List<WItemTouchHelperPlus.RecoverAnimation> recoverAnimationList,

                    int actionState, float dX, float dY) {

        final int recoverAnimSize = recoverAnimationList.size();

        for (int i = 0; i < recoverAnimSize; i++) {

            final WItemTouchHelperPlus.RecoverAnimation anim = recoverAnimationList.get(i);

            final int count = c.save();

            onChildDrawOver(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState,

                    false);

            c.restoreToCount(count);

        }

        if (selected != null) {

            final int count = c.save();

            onChildDrawOver(c, parent, selected, dX, dY, actionState, true);

            c.restoreToCount(count);

        }

        boolean hasRunningAnimation = false;

        for (int i = recoverAnimSize - 1; i >= 0; i--) {

            final WItemTouchHelperPlus.RecoverAnimation anim = recoverAnimationList.get(i);

            if (anim.mEnded && !anim.mIsPendingCleanup) {

                recoverAnimationList.remove(i);

            } else if (!anim.mEnded) {

                hasRunningAnimation = true;

            }

        }

        if (hasRunningAnimation) {

            parent.invalidate();

        }

    }



    /**

     * Called by the WItemTouchHelperPlus when the user interaction with an element is over and it

     * also completed its animation.

     * <p>

     * 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, RecyclerView.ViewHolder, float, float, int,

     * boolean)} or

     * {@link #onChildDrawOver(Canvas, RecyclerView, RecyclerView.ViewHolder, float, float, int, boolean)}.

     *

     * @param recyclerView The RecyclerView which is controlled by the WItemTouchHelperPlus.

     * @param viewHolder   The View that was interacted by the user.

     */

    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {

        sUICallback.clearView(viewHolder.itemView);

    }



    /**

     * Called by WItemTouchHelperPlus on RecyclerView's onDraw callback.

     * <p>

     * If you would like to customize how your View's respond to user interactions, this is

     * a good place to override.

     * <p>

     * Default implementation translates the child by the given <code>dX</code>,

     * <code>dY</code>.

     * WItemTouchHelperPlus also takes care of drawing the child after other children if it is being

     * dragged. This is done using child re-ordering mechanism. On platforms prior to L, this

     * is

     * achieved via {@link android.view.ViewGroup#getChildDrawingOrder(int, int)} and on L

     * and after, it changes View's elevation value to be greater than all other children.)

     *

     * @param c                 The canvas which RecyclerView is drawing its children

     * @param recyclerView      The RecyclerView to which WItemTouchHelperPlus is attached to

     * @param viewHolder        The ViewHolder which is being interacted by the User or it was

     *                          interacted and simply animating to its original position

     * @param dX                The amount of horizontal displacement caused by user's action

     * @param dY                The amount of vertical displacement caused by user's action

     * @param actionState       The type of interaction on the View. Is either {@link

     *                          #ACTION_STATE_DRAG} or {@link #ACTION_STATE_SWIPE}.

     * @param isCurrentlyActive True if this view is currently being controlled by the user or

     *                          false it is simply animating back to its original state.

     * @see #onChildDrawOver(Canvas, RecyclerView, RecyclerView.ViewHolder, float, float, int,

     * boolean)

     */

    public void onChildDraw(Canvas c, RecyclerView recyclerView,

                            RecyclerView.ViewHolder viewHolder,

                            float dX, float dY, int actionState, boolean isCurrentlyActive) {

        sUICallback.onDraw(c, recyclerView, viewHolder.itemView, dX, dY, actionState,

                isCurrentlyActive);

    }



    /**

     * Called by WItemTouchHelperPlus on RecyclerView's onDraw callback.

     * <p>

     * If you would like to customize how your View's respond to user interactions, this is

     * a good place to override.

     * <p>

     * Default implementation translates the child by the given <code>dX</code>,

     * <code>dY</code>.

     * WItemTouchHelperPlus also takes care of drawing the child after other children if it is being

     * dragged. This is done using child re-ordering mechanism. On platforms prior to L, this

     * is

     * achieved via {@link android.view.ViewGroup#getChildDrawingOrder(int, int)} and on L

     * and after, it changes View's elevation value to be greater than all other children.)

     *

     * @param c                 The canvas which RecyclerView is drawing its children

     * @param recyclerView      The RecyclerView to which WItemTouchHelperPlus is attached to

     * @param viewHolder        The ViewHolder which is being interacted by the User or it was

     *                          interacted and simply animating to its original position

     * @param dX                The amount of horizontal displacement caused by user's action

     * @param dY                The amount of vertical displacement caused by user's action

     * @param actionState       The type of interaction on the View. Is either {@link

     *                          #ACTION_STATE_DRAG} or {@link #ACTION_STATE_SWIPE}.

     * @param isCurrentlyActive True if this view is currently being controlled by the user or

     *                          false it is simply animating back to its original state.

     * @see #onChildDrawOver(Canvas, RecyclerView, RecyclerView.ViewHolder, float, float, int,

     * boolean)

     */

    public void onChildDrawOver(Canvas c, RecyclerView recyclerView,

                                RecyclerView.ViewHolder viewHolder,

                                float dX, float dY, int actionState, boolean isCurrentlyActive) {

        sUICallback.onDrawOver(c, recyclerView, viewHolder.itemView, dX, dY, actionState,

                isCurrentlyActive);

    }



    /**

     * Called by the WItemTouchHelperPlus when user action finished on a ViewHolder and now the View

     * will be animated to its final position.

     * <p>

     * Default implementation uses ItemAnimator's duration values. If

     * <code>animationType</code> is {@link #ANIMATION_TYPE_DRAG}, it returns

     * {@link RecyclerView.ItemAnimator#getMoveDuration()}, otherwise, it returns

     * {@link RecyclerView.ItemAnimator#getRemoveDuration()}. If RecyclerView does not have

     * any {@link RecyclerView.ItemAnimator} attached, this method returns

     * {@code DEFAULT_DRAG_ANIMATION_DURATION} or {@code DEFAULT_SWIPE_ANIMATION_DURATION}

     * depending on the animation type.

     *

     * @param recyclerView  The RecyclerView to which the WItemTouchHelperPlus is attached to.

     * @param animationType The type of animation. Is one of {@link #ANIMATION_TYPE_DRAG},

     *                      {@link #ANIMATION_TYPE_SWIPE_CANCEL} or

     *                      {@link #ANIMATION_TYPE_SWIPE_SUCCESS}.

     * @param animateDx     The horizontal distance that the animation will offset

     * @param animateDy     The vertical distance that the animation will offset

     * @return The duration for the animation

     */

    public long getAnimationDuration(RecyclerView recyclerView, int animationType,

                                     float animateDx, float animateDy) {

        final RecyclerView.ItemAnimator itemAnimator = recyclerView.getItemAnimator();

        if (itemAnimator == null) {

            return animationType == ANIMATION_TYPE_DRAG ? DEFAULT_DRAG_ANIMATION_DURATION

                    : DEFAULT_SWIPE_ANIMATION_DURATION;

        } else {

            return animationType == ANIMATION_TYPE_DRAG ? itemAnimator.getMoveDuration()

                    : itemAnimator.getRemoveDuration();

        }

    }



    /**

     * Called by the WItemTouchHelperPlus when user is dragging a view out of bounds.

     * <p>

     * You can override this method to decide how much RecyclerView should scroll in response

     * to this action. Default implementation calculates a value based on the amount of View

     * out of bounds and the time it spent there. The longer user keeps the View out of bounds,

     * the faster the list will scroll. Similarly, the larger portion of the View is out of

     * bounds, the faster the RecyclerView will scroll.

     *

     * @param recyclerView        The RecyclerView instance to which WItemTouchHelperPlus is

     *                            attached to.

     * @param viewSize            The total size of the View in scroll direction, excluding

     *                            item decorations.

     * @param viewSizeOutOfBounds The total size of the View that is out of bounds. This value

     *                            is negative if the View is dragged towards left or top edge.

     * @param totalSize           The total size of RecyclerView in the scroll direction.

     * @param msSinceStartScroll  The time passed since View is kept out of bounds.

     * @return The amount that RecyclerView should scroll. Keep in mind that this value will

     * be passed to {@link RecyclerView#scrollBy(int, int)} method.

     */

    public int interpolateOutOfBoundsScroll(RecyclerView recyclerView,

                                            int viewSize, int viewSizeOutOfBounds,

                                            int totalSize, long msSinceStartScroll) {

        final int maxScroll = getMaxDragScroll(recyclerView);

        final int absOutOfBounds = Math.abs(viewSizeOutOfBounds);

        final int direction = (int) Math.signum(viewSizeOutOfBounds);

        // might be negative if other direction

        float outOfBoundsRatio = Math.min(1f, 1f * absOutOfBounds / viewSize);

        final int cappedScroll = (int) (direction * maxScroll

                * sDragViewScrollCapInterpolator.getInterpolation(outOfBoundsRatio));

        final float timeRatio;

        if (msSinceStartScroll > DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS) {

            timeRatio = 1f;

        } else {

            timeRatio = (float) msSinceStartScroll / DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS;

        }

        final int value = (int) (cappedScroll * sDragScrollInterpolator

                .getInterpolation(timeRatio));

        if (value == 0) {

            return viewSizeOutOfBounds > 0 ? 1 : -1;

        }

        return value;

    }

}



/**

 * A simple wrapper to the default Callback which you can construct with drag and swipe

 * directions and this class will handle the flag callbacks. You should still override onMove

 * or

 * onSwiped depending on your use case.

 * <p>

 * <pre>

 * WItemTouchHelperPlus mIth = new WItemTouchHelperPlus(

 *     new WItemTouchHelperPlus.SimpleCallback(WItemTouchHelperPlus.UP | WItemTouchHelperPlus.DOWN,

 *         WItemTouchHelperPlus.LEFT) {

 *         public abstract boolean onMove(RecyclerView recyclerView,

 *             ViewHolder viewHolder, ViewHolder target) {

 *             final int fromPos = viewHolder.getAdapterPosition();

 *             final int toPos = target.getAdapterPosition();

 *             // move item in `fromPos` to `toPos` in adapter.

 *             return true;// true if moved, false otherwise

 *         }

 *         public void onSwiped(ViewHolder viewHolder, int direction) {

 *             // remove from adapter

 *         }

 * });

 * </pre>

 */

public abstract static class SimpleCallback extends WItemTouchHelperPlus.Callback {



    private int mDefaultSwipeDirs;



    private int mDefaultDragDirs;



    /**

     * Creates a Callback for the given drag and swipe allowance. These values serve as

     * defaults

     * and if you want to customize behavior per ViewHolder, you can override

     * {@link #getSwipeDirs(RecyclerView, RecyclerView.ViewHolder)}

     * and / or {@link #getDragDirs(RecyclerView, RecyclerView.ViewHolder)}.

     *

     * @param dragDirs  Binary OR of direction flags in which the Views can be dragged. Must be

     *                  composed of {@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link

     *                  #END},

     *                  {@link #UP} and {@link #DOWN}.

     * @param swipeDirs Binary OR of direction flags in which the Views can be swiped. Must be

     *                  composed of {@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link

     *                  #END},

     *                  {@link #UP} and {@link #DOWN}.

     */

    public SimpleCallback(int dragDirs, int swipeDirs) {

        mDefaultSwipeDirs = swipeDirs;

        mDefaultDragDirs = dragDirs;

    }



    /**

     * Updates the default swipe directions. For example, you can use this method to toggle

     * certain directions depending on your use case.

     *

     * @param defaultSwipeDirs Binary OR of directions in which the ViewHolders can be swiped.

小结

有了这么多优秀的开发工具,可以做出更高质量的Android应用。

当然了,“打铁还需自身硬”,想要写出优秀的代码,最重要的一点还是自身的技术水平,不然用再好的工具也不能发挥出它的全部实力。

在这里我也分享一份大佬自己收录整理的Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。

总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

E_MS;

        }

        final int value = (int) (cappedScroll * sDragScrollInterpolator

                .getInterpolation(timeRatio));

        if (value == 0) {

            return viewSizeOutOfBounds > 0 ? 1 : -1;

        }

        return value;

    }

}



/**

 * A simple wrapper to the default Callback which you can construct with drag and swipe

 * directions and this class will handle the flag callbacks. You should still override onMove

 * or

 * onSwiped depending on your use case.

 * <p>

 * <pre>

 * WItemTouchHelperPlus mIth = new WItemTouchHelperPlus(

 *     new WItemTouchHelperPlus.SimpleCallback(WItemTouchHelperPlus.UP | WItemTouchHelperPlus.DOWN,

 *         WItemTouchHelperPlus.LEFT) {

 *         public abstract boolean onMove(RecyclerView recyclerView,

 *             ViewHolder viewHolder, ViewHolder target) {

 *             final int fromPos = viewHolder.getAdapterPosition();

 *             final int toPos = target.getAdapterPosition();

 *             // move item in `fromPos` to `toPos` in adapter.

 *             return true;// true if moved, false otherwise

 *         }

 *         public void onSwiped(ViewHolder viewHolder, int direction) {

 *             // remove from adapter

 *         }

 * });

 * </pre>

 */

public abstract static class SimpleCallback extends WItemTouchHelperPlus.Callback {



    private int mDefaultSwipeDirs;



    private int mDefaultDragDirs;



    /**

     * Creates a Callback for the given drag and swipe allowance. These values serve as

     * defaults

     * and if you want to customize behavior per ViewHolder, you can override

     * {@link #getSwipeDirs(RecyclerView, RecyclerView.ViewHolder)}

     * and / or {@link #getDragDirs(RecyclerView, RecyclerView.ViewHolder)}.

     *

     * @param dragDirs  Binary OR of direction flags in which the Views can be dragged. Must be

     *                  composed of {@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link

     *                  #END},

     *                  {@link #UP} and {@link #DOWN}.

     * @param swipeDirs Binary OR of direction flags in which the Views can be swiped. Must be

     *                  composed of {@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link

     *                  #END},

     *                  {@link #UP} and {@link #DOWN}.

     */

    public SimpleCallback(int dragDirs, int swipeDirs) {

        mDefaultSwipeDirs = swipeDirs;

        mDefaultDragDirs = dragDirs;

    }



    /**

     * Updates the default swipe directions. For example, you can use this method to toggle

     * certain directions depending on your use case.

     *

     * @param defaultSwipeDirs Binary OR of directions in which the ViewHolders can be swiped.

小结

有了这么多优秀的开发工具,可以做出更高质量的Android应用。

当然了,“打铁还需自身硬”,想要写出优秀的代码,最重要的一点还是自身的技术水平,不然用再好的工具也不能发挥出它的全部实力。

在这里我也分享一份大佬自己收录整理的Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。

总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 20
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RecyclerView侧滑菜单可以通过ItemTouchHelper类来实现。 首先,需要创建一个实现ItemTouchHelper.Callback的类,重写其中的方法,用于处理拖拽和侧滑等操作。在其中,我们需要实现onSwiped方法,用于处理RecyclerView中的侧滑操作。 可以通过实现onChildDraw方法来绘制侧滑菜单,例如使用Canvas绘制背景和图标等。然后在onSwiped方法中,通过ViewHolder.getAdapterPosition()获取当前侧滑的位置,然后再调用Adapter的remove方法来删除对应的数据。最后,需要在Adapter中实现onCreateViewHolder方法,在其中绑定侧滑菜单的布局和操作。 下面是一个简单的示例代码: ```java public class SwipeController extends ItemTouchHelper.Callback { private RecyclerView.Adapter adapter; public SwipeController(RecyclerView.Adapter adapter) { this.adapter = adapter; } @Override public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { return makeMovementFlags(0, ItemTouchHelper.LEFT); } @Override public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) { return false; } @Override public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { int position = viewHolder.getAdapterPosition(); adapter.notifyItemRemoved(position); adapter.notifyItemRangeChanged(position, adapter.getItemCount()); } @Override public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { View itemView = viewHolder.itemView; Paint p = new Paint(); if (dX < 0) { p.setColor(Color.RED); RectF background = new RectF((float)itemView.getRight() + dX, (float)itemView.getTop(), (float)itemView.getRight(), (float)itemView.getBottom()); c.drawRect(background, p); Drawable icon = ContextCompat.getDrawable(adapter.getContext(), R.drawable.ic_delete); int iconWidth = icon.getIntrinsicWidth(); int iconHeight = icon.getIntrinsicHeight(); int left = itemView.getRight() - iconWidth - 32; int top = itemView.getTop() + (itemView.getHeight() - iconHeight) / 2; int right = itemView.getRight() - 32; int bottom = top + iconHeight; icon.setBounds(left, top, right, bottom); icon.draw(c); } super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } } ``` 在Activity或Fragment中,可以将SwipeController作为参数传递给ItemTouchHelper,然后调用ItemTouchHelper.attachToRecyclerView方法来绑定RecyclerView。 ```java RecyclerView recyclerView = findViewById(R.id.recycler_view); Adapter adapter = new Adapter(data); recyclerView.setAdapter(adapter); SwipeController swipeController = new SwipeController(adapter); ItemTouchHelper itemTouchHelper = new ItemTouchHelper(swipeController); itemTouchHelper.attachToRecyclerView(recyclerView); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值