SHARE
TWEET

swipeableRecyclerListener

a guest Feb 16th, 2015 167 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. public class SwipeableRecyclerViewTouchListener implements RecyclerView.OnItemTouchListener {
  2.     // Cached ViewConfiguration and system-wide constant values
  3.     private int mSlop;
  4.     private int mMinFlingVelocity;
  5.     private int mMaxFlingVelocity;
  6.     private long mAnimationTime;
  7.     // Fixed properties
  8.     private RecyclerView mRecyclerView;
  9.     private SwipeListener mSwipeListener;
  10.     private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero
  11.     // Transient properties
  12.     private List<PendingDismissData> mPendingDismisses = new ArrayList<>();
  13.     private int mDismissAnimationRefCount = 0;
  14.     private float mDownX;
  15.     private float mDownY;
  16.     private boolean mSwiping;
  17.     private int mSwipingSlop;
  18.     private VelocityTracker mVelocityTracker;
  19.     private int mDownPosition;
  20.     private View mDownView;
  21.     private boolean mPaused;
  22.     private float mFinalDelta;
  23.     /**
  24.      * Constructs a new swipe touch listener for the given {@link android.support.v7.widget.RecyclerView}
  25.      *
  26.      * @param recyclerView The recycler view whose items should be dismissable by swiping.
  27.      * @param listener The listener for the swipe events.
  28.      */
  29.     public SwipeableRecyclerViewTouchListener(RecyclerView recyclerView, SwipeListener listener) {
  30.         ViewConfiguration vc = ViewConfiguration.get(recyclerView.getContext());
  31.         mSlop = vc.getScaledTouchSlop();
  32.         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16;
  33.         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
  34.         mAnimationTime = recyclerView.getContext().getResources().getInteger(
  35.                 android.R.integer.config_shortAnimTime);
  36.         mRecyclerView = recyclerView;
  37.         mSwipeListener = listener;
  38. /**
  39.  * This will ensure that this SwipeableRecyclerViewTouchListener is paused during list view scrolling.
  40.  * If a scroll listener is already assigned, the caller should still pass scroll changes through
  41.  * to this listener.
  42.  */
  43.         mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
  44.             @Override
  45.             public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
  46.                 setEnabled(newState != RecyclerView.SCROLL_STATE_DRAGGING);
  47.             }
  48.             @Override
  49.             public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
  50.             }
  51.         });
  52.     }
  53.     /**
  54.      * Enables or disables (pauses or resumes) watching for swipe-to-dismiss gestures.
  55.      *
  56.      * @param enabled Whether or not to watch for gestures.
  57.      */
  58.     public void setEnabled(boolean enabled) {
  59.         mPaused = !enabled;
  60.     }
  61.     @Override
  62.     public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent motionEvent) {
  63.         return handleTouchEvent(motionEvent);
  64.     }
  65.     @Override
  66.     public void onTouchEvent(RecyclerView rv, MotionEvent motionEvent) {
  67.         handleTouchEvent(motionEvent);
  68.     }
  69.     private boolean handleTouchEvent(MotionEvent motionEvent) {
  70.         if (mViewWidth < 2) {
  71.             mViewWidth = mRecyclerView.getWidth();
  72.         }
  73.         switch (motionEvent.getActionMasked()) {
  74.             case MotionEvent.ACTION_DOWN: {
  75.                 if (mPaused) {
  76.                     break;
  77.                 }
  78. // Find the child view that was touched (perform a hit test)
  79.                 Rect rect = new Rect();
  80.                 int childCount = mRecyclerView.getChildCount();
  81.                 int[] listViewCoords = new int[2];
  82.                 mRecyclerView.getLocationOnScreen(listViewCoords);
  83.                 int x = (int) motionEvent.getRawX() - listViewCoords[0];
  84.                 int y = (int) motionEvent.getRawY() - listViewCoords[1];
  85.                 View child;
  86.                 for (int i = 0; i < childCount; i++) {
  87.                     child = mRecyclerView.getChildAt(i);
  88.                     child.getHitRect(rect);
  89.                     if (rect.contains(x, y)) {
  90.                         mDownView = child;
  91.                         break;
  92.                     }
  93.                 }
  94.                 if (mDownView != null) {
  95.                     mDownX = motionEvent.getRawX();
  96.                     mDownY = motionEvent.getRawY();
  97.                     mDownPosition = mRecyclerView.getChildPosition(mDownView);
  98.                     if (mSwipeListener.canSwipe(mDownPosition)) {
  99.                         mVelocityTracker = VelocityTracker.obtain();
  100.                         mVelocityTracker.addMovement(motionEvent);
  101.                     } else {
  102.                         mDownView = null;
  103.                     }
  104.                 }
  105.                 break;
  106.             }
  107.  
  108.             case MotionEvent.ACTION_CANCEL: {
  109.                 if (mVelocityTracker == null) {
  110.                     break;
  111.                 }
  112.                 if (mDownView != null && mSwiping) {
  113. // cancel
  114.                     mDownView.animate()
  115.                             .translationX(0)
  116.                             .alpha(1)
  117.                             .setDuration(mAnimationTime)
  118.                             .setListener(null);
  119.                 }
  120.                 mVelocityTracker.recycle();
  121.                 mVelocityTracker = null;
  122.                 mDownX = 0;
  123.                 mDownY = 0;
  124.                 mDownView = null;
  125.                 mDownPosition = ListView.INVALID_POSITION;
  126.                 mSwiping = false;
  127.                 break;
  128.             }
  129.             case MotionEvent.ACTION_UP: {
  130.                 if (mVelocityTracker == null) {
  131.                     break;
  132.                 }
  133.                 mFinalDelta = motionEvent.getRawX() - mDownX;
  134.                 mVelocityTracker.addMovement(motionEvent);
  135.                 mVelocityTracker.computeCurrentVelocity(1000);
  136.                 float velocityX = mVelocityTracker.getXVelocity();
  137.                 float absVelocityX = Math.abs(velocityX);
  138.                 float absVelocityY = Math.abs(mVelocityTracker.getYVelocity());
  139.                 boolean dismiss = false;
  140.                 boolean dismissRight = false;
  141.                 if (Math.abs(mFinalDelta) > mViewWidth / 2 && mSwiping) {
  142.                     dismiss = true;
  143.                     dismissRight = mFinalDelta > 0;
  144.                 } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity
  145.                         && absVelocityY < absVelocityX && mSwiping) {
  146. // dismiss only if flinging in the same direction as dragging
  147.                     dismiss = (velocityX < 0) == (mFinalDelta < 0);
  148.                     dismissRight = mVelocityTracker.getXVelocity() > 0;
  149.                 }
  150.  
  151.  
  152.                 if(!dismiss) {
  153.                     mDownView.animate()
  154.                             .translationX(0)
  155.                             .alpha(1)
  156.                             .setDuration(mAnimationTime)
  157.                             .setListener(null);
  158.  
  159.  
  160.                     mVelocityTracker.recycle();
  161.                     mVelocityTracker = null;
  162.                     mDownX = 0;
  163.                     mDownY = 0;
  164.                     mDownView = null;
  165.                     mDownPosition = ListView.INVALID_POSITION;
  166.                     mSwiping = false;
  167.  
  168.                     Log.d("if!dismiss", "here now");
  169.                 }
  170.  
  171.  
  172.                 if (dismiss && mDownPosition != ListView.INVALID_POSITION) {
  173. // dismiss
  174.                     final View downView = mDownView; // mDownView gets null'd before animation ends
  175.                     final int downPosition = mDownPosition;
  176.                     ++mDismissAnimationRefCount;
  177.  
  178.  
  179.                     final SweetAlertDialog dialog = new SweetAlertDialog(mRecyclerView.getContext(), SweetAlertDialog.WARNING_TYPE);
  180.                     dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
  181.                         @Override
  182.                         public void onDismiss(DialogInterface dialog) {
  183.                             mRecyclerView.invalidateItemDecorations();
  184.                         }
  185.                     });
  186.                     final boolean finalDismissRight = dismissRight;
  187.                     dialog
  188.                             .setTitleText("Are you sure?")
  189.                             .setContentText("Won't be able to recover this file!")
  190.                             .setCancelText("No,cancel plx!")
  191.                             .setConfirmText("Yes,delete it!")
  192.                             .showCancelButton(true)
  193.                             .setCancelClickListener(new SweetAlertDialog.OnSweetClickListener() {
  194.                                 @Override
  195.                                 public void onClick(SweetAlertDialog sDialog) {
  196.                                     sDialog.dismiss();
  197.                                     dialog.dismiss();
  198.  
  199.                                     if(mDownView != null) {
  200.                                         mDownView.animate()
  201.                                                 .translationX(0)
  202.                                                 .alpha(1)
  203.                                                 .setDuration(mAnimationTime)
  204.                                                 .setListener(null);
  205.                                         Log.d("CancelClickListener", "If statement");
  206.                                         mRecyclerView.invalidateItemDecorations();
  207.                                     }
  208.  
  209.                                     else {
  210.                                         downView.animate()
  211.                                                 .translationX(0)
  212.                                                 .alpha(1)
  213.                                                 .setDuration(mAnimationTime)
  214.                                                 .setListener(null);
  215.                                         Log.d("CancelClickListener", "Else statement");
  216.                                         mRecyclerView.invalidateItemDecorations();
  217.                                     }
  218.  
  219.                                         mVelocityTracker.recycle();
  220.                                         mVelocityTracker = null;
  221.                                         mDownX = 0;
  222.                                         mDownY = 0;
  223.                                         mDownView = null;
  224.                                         mDownPosition = ListView.INVALID_POSITION;
  225.                                         mSwiping = false;
  226.  
  227.                                     Log.d("CancelClickListener", "After if and else statement");
  228.                                     mRecyclerView.invalidateItemDecorations();
  229.                                     sDialog.dismiss();
  230.                                 }
  231.                             })
  232.                             .setConfirmClickListener(new SweetAlertDialog.OnSweetClickListener() {
  233.                                 @Override
  234.                                 public void onClick(SweetAlertDialog sweetAlertDialog) {
  235.                                     dialog.cancel();
  236.                                     new SweetAlertDialog(sweetAlertDialog.getContext(), SweetAlertDialog.SUCCESS_TYPE)
  237.                                             .setTitleText("Good job!")
  238.                                             .setContentText("You clicked the button!")
  239.                                             .show();
  240.  
  241.                                     mDownView.animate()
  242.                                             .translationX(finalDismissRight ? mViewWidth : -mViewWidth)
  243.                                             .alpha(0)
  244.                                             .setDuration(mAnimationTime)
  245.                                             .setListener(new AnimatorListenerAdapter() {
  246.                                                 @Override
  247.                                                 public void onAnimationEnd(Animator animation) {
  248.                                                     performDismiss(downView, downPosition);
  249.                                                     Log.d("ConfirmClickListener", "OnAnimationEnd");
  250.                                                 }
  251.                                             });
  252.  
  253.                                     mVelocityTracker.recycle();
  254.                                     mVelocityTracker = null;
  255.                                     mDownX = 0;
  256.                                     mDownY = 0;
  257.                                     mDownView = null;
  258.                                     mDownPosition = ListView.INVALID_POSITION;
  259.                                     mSwiping = false;
  260.  
  261.                                 }
  262.                             })
  263.                             .show();
  264.                 }
  265.                 break;
  266.             }
  267.             case MotionEvent.ACTION_MOVE: {
  268.                 if (mVelocityTracker == null || mPaused) {
  269.                     break;
  270.                 }
  271.                 mVelocityTracker.addMovement(motionEvent);
  272.                 float deltaX = motionEvent.getRawX() - mDownX;
  273.                 float deltaY = motionEvent.getRawY() - mDownY;
  274.                 if (!mSwiping && Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) {
  275.                     mSwiping = true;
  276.                     mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop);
  277.                 }
  278.                 if (mSwiping) {
  279.                     mDownView.setTranslationX(deltaX - mSwipingSlop);
  280.                     mDownView.setAlpha(Math.max(0f, Math.min(1f,
  281.                             1f - Math.abs(deltaX) / mViewWidth)));
  282.                     return true;
  283.                 }
  284.                 break;
  285.             }
  286.         }
  287.         return false;
  288.     }
  289.     private void performDismiss(final View dismissView, final int dismissPosition) {
  290. // Animate the dismissed list item to zero-height and fire the dismiss callback when
  291. // all dismissed list item animations have completed. This triggers layout on each animation
  292. // frame; in the future we may want to do something smarter and more performant.
  293.  
  294.         final ViewGroup.LayoutParams lp = dismissView.getLayoutParams();
  295.         final int originalHeight = dismissView.getHeight();
  296.         ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime);
  297.         animator.addListener(new AnimatorListenerAdapter() {
  298.             @Override
  299.             public void onAnimationEnd(Animator animation) {
  300.                 --mDismissAnimationRefCount;
  301.                 if (mDismissAnimationRefCount == 0) {
  302. // No active animations, process all pending dismisses.
  303. // Sort by descending position
  304.  
  305.                 Collections.sort(mPendingDismisses);
  306.                 int[] dismissPositions = new int[mPendingDismisses.size()];
  307.                 for (int i = mPendingDismisses.size() - 1; i >= 0; i--) {
  308.                     dismissPositions[i] = mPendingDismisses.get(i).position;
  309.                 }
  310.                 if (mFinalDelta > 0) {
  311.                     mSwipeListener.onDismissedBySwipeRight(mRecyclerView, dismissPositions);
  312.                 } else {
  313.                     mSwipeListener.onDismissedBySwipeLeft(mRecyclerView, dismissPositions);
  314.                 }
  315. // Reset mDownPosition to avoid MotionEvent.ACTION_UP trying to start a dismiss
  316. // animation with a stale position
  317.                 mDownPosition = ListView.INVALID_POSITION;
  318.                 ViewGroup.LayoutParams lp;
  319.                 for (PendingDismissData pendingDismiss : mPendingDismisses) {
  320. // Reset view presentation
  321.                     pendingDismiss.view.setAlpha(1f);
  322.                     pendingDismiss.view.setTranslationX(0);
  323.                     lp = pendingDismiss.view.getLayoutParams();
  324.                     lp.height = originalHeight;
  325.                     pendingDismiss.view.setLayoutParams(lp);
  326.                 }
  327. // Send a cancel event
  328.                 long time = SystemClock.uptimeMillis();
  329.                 MotionEvent cancelEvent = MotionEvent.obtain(time, time,
  330.                         MotionEvent.ACTION_CANCEL, 0, 0, 0);
  331.                 mRecyclerView.dispatchTouchEvent(cancelEvent);
  332.                 mPendingDismisses.clear();
  333.             }
  334.         }
  335.     });
  336.     animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  337.         @Override
  338.         public void onAnimationUpdate(ValueAnimator valueAnimator) {
  339.             lp.height = (Integer) valueAnimator.getAnimatedValue();
  340.             dismissView.setLayoutParams(lp);
  341.         }
  342.     });
  343.     mPendingDismisses.add(new PendingDismissData(dismissPosition, dismissView));
  344.     animator.start();
  345.  
  346.  
  347.     }
  348.     /**
  349.      * The callback interface used by {@link SwipeableRecyclerViewTouchListener} to inform its client
  350.      * about a swipe of one or more list item positions.
  351.      */
  352.     public interface SwipeListener {
  353.         /**
  354.          * Called to determine whether the given position can be swiped.
  355.          */
  356.         boolean canSwipe(int position);
  357.         /**
  358.          * Called when the item has been dismissed by swiping to the left.
  359.          *
  360.          * @param recyclerView The originating {@link android.support.v7.widget.RecyclerView}.
  361.          * @param reverseSortedPositions An array of positions to dismiss, sorted in descending
  362.          * order for convenience.
  363.          */
  364.         void onDismissedBySwipeLeft(RecyclerView recyclerView, int[] reverseSortedPositions);
  365.         /**
  366.          * Called when the item has been dismissed by swiping to the right.
  367.          *
  368.          * @param recyclerView The originating {@link android.support.v7.widget.RecyclerView}.
  369.          * @param reverseSortedPositions An array of positions to dismiss, sorted in descending
  370.          * order for convenience.
  371.          */
  372.         void onDismissedBySwipeRight(RecyclerView recyclerView, int[] reverseSortedPositions);
  373.     }
  374.     class PendingDismissData implements Comparable<PendingDismissData> {
  375.         public int position;
  376.         public View view;
  377.         public PendingDismissData(int position, View view) {
  378.             this.position = position;
  379.             this.view = view;
  380.         }
  381.         @Override
  382.         public int compareTo(@NonNull PendingDismissData other) {
  383. // Sort by descending position
  384.             return other.position - position;
  385.         }
  386.     }
  387. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top