Android ViewPager 和H5 水平滑动冲突

571 阅读3分钟

问题:产品中频道页中有个频道是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());
            }
        });