问题:产品中频道页中有个频道是H5,项目里使用viewpager + fragment来实现多频道的切换,其中一个fragment里面有一个webview,这个webview加载的H5页面有左右滑动行情列表,外层父控件(viewpager)会拦截并且消费左右滑动事件,所以H5中的左右滑动轮播图就不能滑动了。
解决方案:
- 第一种方案是禁止左右滑动
当频道页是H5的时候,就禁止外层viewpager左右滑动,即webview调用requestDisallowInterceptTouchEvent(true)不要让父布局拦截事件即可。但是这种方案太过暴力,如果产品经理同意也行。
-
第二种方案是原生native与H5进行交互
由H5告诉native自身轮播图的位置,然后在webview的onTouchListener的onTouch方法里做相应的处理,如果滑动手势在这个范围,则requestDisallowInterceptTouchEvent(true)不要让父布局拦截事件。反之,则让父布局拦截事件。这种方案局限性很多,如果H5需求是明确的,只是固定在某一区域有左右滑动的轮播,并且未来也不会改变太多,那这种方案可行,但是如果H5页面有很多可以左右滑动的控件,并且未来需求不一定,那么这些控件的范围就都得告诉原生。而且客户端的范围判断也会随着H5的变化跟着变化,这样的话就不可取。具体参考www.jianshu.com/p/a6f9d4046…
-
第三种方案同样也是原生native与H5进行交互,原理相似。但是不同于方案二,H5不需要告诉原生native具体轮播控件的范围,原生native只需要定义好是否让父布局拦截事件的方法供H5调用即可。H5上的轮播控件只需要在相应的touchstart和touchmove调起原生的方法即可。具体可参考www.jianshu.com/p/818d566c4…
// 轮播滚动
var mySwiper = $('.slidepics').swiper({
loop : true,
pagination: '.dotted p',
paginationClickable: true,
spaceBetween: 30,
autoplay:3000,
autoplayDisableOnInteraction:false,
onSliderMove:function(swiper) {
//如果光用此方法来申请app原生控件不拦截事件,在快速滑动的时候可能抢不到事件
//还要配合下面的touch事件来获取
isBeingDrag = true;
window.Android.requestEvent(true);
}
});
// 轮播滚动事件监听
$('.slidepics').on('touchstart touchmove touchend touchcancel' , function(event) {
var touch = event.originalEvent.targetTouches[0];
switch (event.type) {
case "touchstart":
mLastClientX = touch.clientX;
mLastClientY = touch.clientY;
isBeingDrag = false;
window.Android.requestEvent(true);
break;
case "touchmove":
if (!isBeingDrag) {
var xDiff = Math.abs(touch.clientX - mLastClientX);
var yDiff = Math.abs(touch.clientY - mLastClientY);
//console.log(xDiff + " " + yDiff);
if (xDiff >= touchSlop) {
isBeingDrag = true;
} else if(yDiff > touchSlop) {
//产生app纵向滑动,父控件不强制请求放行事件,这段逻辑主要是不影响外层垂直滑动控件的滑动
//js端控制滑动与app端的标准不一样,所以结合上面的onSliderMove:function方法来判断
//如果H5端控件已经产生滑动时则必须请求父控件放行事件
//如果有些开源控件没有类似onSliderMove方法时,只需提供控件是否产生滑动就行,原理都是一样
isBeingDrag = true;
window.Android.requestEvent(false);
}
}
break;
case "touchend":
window.Android.requestEvent(false);
break;
}
});
//app端的滑动值
var touchSlop = 0;
//初次按下时的x, y轴值
var mLastClientX, mLastClientY;
//是否处于滑动中
var isBeingDrag = false;
//js附值,在web加载完成时将android的滑动单位值传给js
function initTouchSlop(appTouchSlop) {
touchSlop = appTouchSlop;
}
/**
* js交互类
*/
private class JsCallback {
@JavascriptInterface
public void requestEvent(boolean request) {
Log.i("you", "requestDisallowInterceptTouchEvent " + request+" "+Thread.currentThread().getName());
mWebView.requestDisallowInterceptTouchEvent(request);
}
}
mWebView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
int touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
StringBuilder jsSb = new StringBuilder("javascript:initTouchSlop('").append(touchSlop).append("')");
mWebView.loadUrl(jsSb.toString());
}
});