11

I have a ViewPager2 with a fragment inside,

in the fragment, I have a custom view with certain touch logic that involves moving the finger.

how do I prevent the ViewPager from swiping while the inner view intercepts the touch event?

override fun onTouchEvent(event: MotionEvent?): Boolean {
    if (event?.action == MotionEvent.ACTION_DOWN || event?.action == MotionEvent.ACTION_MOVE) {
       //Do some stuff here
    }
    return true
}

while swiping this view the view pager still swipes to other pages.

Vasili Fedotov
  • 1,091
  • 14
  • 31

3 Answers3

7

Set OnTouchListener for inner view. In onTouch() method, call:

viewPager.requestDisallowInterceptTouchEvent(true)

ViewPager handles its swiping motion in onInterceptTouchEvent(). Above code prevents ViewPager from calling onInterceptTouchEvent(). When you're swiping, ViewPager returns true in onInterceptTouchEvent() which also prevents touch events to be passed to child views. Therefore disallowing intercept allows child views to handle touch events.

Set back to false when the inner view is not being touched.

From my experience, onInterceptTouchEvent() prevents onTouchEvent(). It does not prevent OnTouchListener. So the key here is to set up OnTouchListener for the inner view.

3

To fix this, you should extend the ViewPager2 like this:

fun ViewPager2.reduceDragSensitivity() {
    val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView")
    recyclerViewField.isAccessible = true
    val recyclerView = recyclerViewField.get(this) as RecyclerView

    val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop")
    touchSlopField.isAccessible = true
    val touchSlop = touchSlopField.get(recyclerView) as Int
    touchSlopField.set(recyclerView, touchSlop * 6) // "6" was obtained experimentally
}

and call reduceDragSensitivity. So the viewpager2 will look like this:

view_pager.reduceDragSensitivity()

Example: https://github.com/ali-sardari/ViewPager2Example

enter image description here

Ali Sardari
  • 351
  • 6
  • 8
1

The following worked for me, for a custom view class that scrolls vertically and horizontally. The ViewPager2 that contains it was grabbing all horizontal movements and turning them into page changes. The call to requestDisallowInterceptTouchEvent below, during creation of the GestureDetector, keeps "regular" scrolls in the custom views but leaves fling gestures still working in the ViewPager2. YMMV, of course. I didn't change the flag back to false anywhere; that seems to be handled in OnTouchUp somewhere.

    /* Called during view construction */
private void addGestureDetection(Context context) {
    mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener() {

        @Override
        public boolean onScroll (MotionEvent e1,
                                 MotionEvent e2,
                                 float distanceX,
                                 float distanceY)
        {
            /* Prevent the ViewPager2 from grabbing all the scroll events and turning
            a horizontal scroll into a view change.  The ViewPager2 still gets flings gestures*/
            getParent().requestDisallowInterceptTouchEvent(true);

            ...
        }
    }
Bob Cram
  • 145
  • 6