The phenomenon of

Recently doing a picture viewer, using horizontal ViewPager2+PhotoView to achieve. But it turns out that when you zoom in on an image and it’s at a non-left/right boundary, you swipe left/right, sometimes PhotoView responds to swipe, sometimes external ViewPager2 responds to swipe.

Analysis of the

After trial and error, we found that when the PhotoView image is at the top or bottom boundary, the finger swipes left and right slightly, triggering the external ViewPager2 slide. View the PhotoView source code for the scroll section:

private OnGestureListener onGestureListener = new OnGestureListener() {
    @Override
    public void onDrag(float dx, float dy) {
        if (mScaleDragDetector.isScaling()) {
            return; // Do not drag if we are already scaling
        }
        if(mOnViewDragListener ! =null) {
            mOnViewDragListener.onDrag(dx, dy);
        }
        mSuppMatrix.postTranslate(dx, dy);
        checkAndDisplayMatrix();

        /* * Here we decide whether to let the ImageView's parent to start taking * over the touch event. * * First we check whether this function is enabled. We never want the * parent to take over if we're scaling. We then check the edge we're  * on, and the direction of the scroll (i.e. if we're pulling against * the edge, aka 'overscrolling', let the parent take over). */
        ViewParent parent = mImageView.getParent();
        if(mAllowParentInterceptOnEdge && ! mScaleDragDetector.isScaling() && ! mBlockParentIntercept) {if (mHorizontalScrollEdge == HORIZONTAL_EDGE_BOTH
                    || (mHorizontalScrollEdge == HORIZONTAL_EDGE_LEFT && dx >= 1f)
                    || (mHorizontalScrollEdge == HORIZONTAL_EDGE_RIGHT && dx <= -1f)
                    || (mVerticalScrollEdge == VERTICAL_EDGE_TOP && dy >= 1f)
                    || (mVerticalScrollEdge == VERTICAL_EDGE_BOTTOM && dy <= -1f)) {
                if(parent ! =null) {
                    parent.requestDisallowInterceptTouchEvent(false); }}}else {
            if(parent ! =null) {
                parent.requestDisallowInterceptTouchEvent(true); }}}... }Copy the code

It can be seen that the internal interception method is used for sliding conflict PhotoView, but the qualification condition is problematic:

if (mHorizontalScrollEdge == HORIZONTAL_EDGE_BOTH
    || (mHorizontalScrollEdge == HORIZONTAL_EDGE_LEFT && dx >= 1f)
    || (mHorizontalScrollEdge == HORIZONTAL_EDGE_RIGHT && dx <= -1f)
    || (mVerticalScrollEdge == VERTICAL_EDGE_TOP && dy >= 1f)
    || (mVerticalScrollEdge == VERTICAL_EDGE_BOTTOM && dy <= -1f))
Copy the code

When zooming in on the lower edge of the edge or, directly call the parent. RequestDisallowInterceptTouchEvent (false); Giving the parent View a chance to intercept logic, and the parent View, in this case ViewPager2, is also very aggressive in intercepting events, causing external ViewPager2 to slide.

This should be a PhotoView bug, and it can also be a problem with external ViewPager.

The solution

Since PhotoView doesn’t have much code, I copied it directly into the project and changed the code in onGestureListener#onDrag:

@Override public void onDrag(float dx, float dy) { if (mScaleDragDetector.isScaling()) { return; // Do not drag if we are already scaling } if (mOnViewDragListener ! = null) { mOnViewDragListener.onDrag(dx, dy); } mSuppMatrix.postTranslate(dx, dy); checkAndDisplayMatrix(); /* * Here we decide whether to let the ImageView's parent to start taking * over the touch event. * * First we check whether this function is enabled. We never want the * parent to take over if we're scaling. We then check the edge we're  * on, and the direction of the scroll (i.e. if we're pulling against * the edge, aka 'overscrolling', let the parent take over). */ ViewParent parent = mImageView.getParent(); / / drag the if (mAllowParentInterceptOnEdge &&! mScaleDragDetector.isScaling() && ! noScaleAndDrag) { float absDx = Math.abs(dx); float absDy = Math.abs(dy); if (mHorizontalScrollEdge == HORIZONTAL_EDGE_BOTH && absDx > absDy || (mHorizontalScrollEdge == HORIZONTAL_EDGE_LEFT && dx >= 1f && absDx > absDy) || (mHorizontalScrollEdge == HORIZONTAL_EDGE_RIGHT && dx <= -1f && absDx > absDy) || (mVerticalScrollEdge == VERTICAL_EDGE_TOP && dy >= 1f && absDy > absDx) || (mVerticalScrollEdge == VERTICAL_EDGE_BOTTOM && dy <= -1f && absDy > absDx || (mVerticalScrollEdge == VERTICAL_EDGE_BOTH) && absDy > absDx) ) { if (parent ! = null) { parent.requestDisallowInterceptTouchEvent(false); } } } else { if (parent ! = null) { parent.requestDisallowInterceptTouchEvent(true); }}}Copy the code

Simple explanation, is in the request parent View continue to intercept conditions, added the sliding direction of the distance > the other direction of the distance judgment condition.