现象
最近在做一个图片查看器,采用水平ViewPager2+PhotoView去实现。但发现当放大图片并且图片处在非左右边界时,手指左右滑动,有时是PhotoView响应滑动,有时是外部ViewPager2响应滑动。
分析
经过反复实验,发现PhotoView图片处在上或下边界时,手指左右滑动稍微斜了一点,便会触发外部ViewPager2滑动。
查看PhotoView源码关于滚动处理部分:
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);
}
}
}
...
}
复制代码
可以看出针对滑动冲突PhotoView采用的是内部拦截法,但是限定条件有问题:
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))
复制代码
当图片缩放处于上边缘或下边缘的时候,直接调用了parent.requestDisallowInterceptTouchEvent(false);
让父View有机会走拦截逻辑,而父View,这里是ViewPager2也很不客气的拦截了事件,导致触发外部ViewPager2滑动。
这个应该算是PhotoView的bug,外部用ViewPager的话也一样会有这个问题。
解决方法
由于PhotoView的代码不多,我直接复制到项目里,修改了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();
//拖动的时候
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);
}
}
}
复制代码
简单说明下,就是在请求父View继续拦截的条件里,增加了(滑动方向的距离>另一方向上的距离)的判定条件。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END