问题场景 在项目的App中有一个ViewPager,它内部包含了WebView,而内部的webview加载了一个可以左右滑动的网页。当我们在网页滑动的时候,会直接切换到下一个WebView的页面,而不是优先响应WebView的滑动,这样一来WebView和ViewPager的滑动就出现了冲突。
但是产品期望场景是当手指落到WebView的可滑动区域是相应WebView的滑动事件,当滑动到边缘之后再相应ViewPager的滑动事件。
解决思路如下:
- 首先响应WebView的滑动事件
- WebView的滑动事件完成,响应父控件的滑动事件
有了以上思路我们就能着手实现了,但是我们怎么能检测到WebView的滑动事件完成了呢?原来WebView的实现者也考虑到了这方面的需求,它暴露了一个方法
@Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
mProvider.getViewDelegate().onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
官方文档描述如下图:
通过上述方法我能就能监测到WebView滑动到了边界,接下来就是对拦截事件的处理了:
在WebView的onTouchEvent事件为ACTION_DOWN时,查找父视图是否是可以滑动的视图(如ViewPager),如果是,则通过调用requestDisallowInterceptTouchEvent(true),请求父视图不要拦截touchEvent事件
如果WebView不再响应内部滑动(即onOverScrolled中clampedX或者clampedY值为true),我们再起调用requestDisallowInterceptTouchEvent(false)请求父视图恢复拦截处理touchEvent事件.
好了思路和方法都有了那我就就开始通过重写WebView的方法来实现我们的功能了,具体代码如下:
class CommonWebView : WebView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
val viewParent = findViewParentIfNeeds(this)
viewParent?.requestDisallowInterceptTouchEvent(true)
}
return super.onTouchEvent(event)
}
override fun onOverScrolled(scrollX: Int, scrollY: Int, clampedX: Boolean, clampedY: Boolean) {
if (clampedX) {//水平判断
val viewParent = findViewParentIfNeeds(this)
viewParent?.requestDisallowInterceptTouchEvent(false)
}
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY)
}
private fun findViewParentIfNeeds(tag: View): ViewParent? {
val parent = tag.parent
if (parent == null) {
return parent
}
return if (parent is ViewPager ||
parent is AbsListView ||
parent is ScrollView ||
parent is HorizontalScrollView ||
parent is GridView
) {
parent
} else {
if (parent is View) {
findViewParentIfNeeds(parent as View)
} else {
parent
}
}
}
}
通过上述处理就能完美解决开篇的问题。