Viewpager与webview滑动冲突的解决方案

场景描述

最近在接触h5与android混合开发时遇到一个问题,在一个activity使用ViewPager+Fragment结构,某个Fragment包含了一个webview。而在这个webview展示的h5里有一个横屏轮播的元素,此时当我们横向滑动的时候,大多数情况下是ViewPager在滑动(这里说是大多数情况是不考虑网页可以横向滑动的特殊情况)。因此我们需要判断并处理事件。

如下图所示,蓝色线条中间的部分是一个轮播。而整个首页的结构是一个Viewpager。


Paste_Image.png

问题分析

其实h5的轮播基本都会支持手滑动事件(指JS控制轮播的滑动),我们要做的就是判断什么时候,touch事件由h5来处理,什么时候由ViewPager来处理,这里就不再讲述android的事件机制,有兴趣的同学可以去老衲之前发布的文章中去找。这里只用到了其中一个知识点,即child如何影响parent的事件处理。所涉及到的方法就是

//当result为true的时候,child会阻止parent获取touch事件,反之则不会影响。
requestDisallowInterceptTouchEvent(result);

而这个方法参数result的值是true还是false,就是今天要踩得坑之一。

Paste_Image.png

主要思路

  1. 首先我们需要确定的是,需要重写谁的onTouch方法,webview还是viewpager,当然是webview,通过webview来主动控制viewpager的事件获取权限。

  2. 接下来,为了判断,我们还需要轮播控件的坐标和范围,这个可以有h5和JS来实现

  3. 范围有了。接下来就是要真正进行touch的坐标判断了。

踩坑1之Java与JS的之间相互调用的顺序

关于android内的java方法与html内的js方法相互调用请大家自行百度。这里要提醒大家的是,当JS与java在进行交互的时候,他们并不是同步执行的。举个栗子

public void doCheck() {
    String call = "javascript:getViewPagerInfo()";
    webview.loadUrl(call);
}

当通过上述方法调用JS的getViewPagerInfo方法时,而JS的getViewPagerInfo方法内部又调用了java的下列方法时。

    @JavascriptInterface
    public void getH5ViewPagerInfo(int x ,int y , int width , int height){
        mPagerDesc = new PagerDesc(y,x,x+width,y+height , 0);
    }

假如我们需要按顺序执行如下两个方法

doCheck();
showToast();

当执行完doCheck的loadurl方法之后,他会去执行showToast,不会等JS回调java的getH5ViewPagerInfo方法执行完再执行。

踩坑2之JS滑动

刚开始接到这个需求的时候,会想的太多,导致刚开始考虑h5的时候顺带把网页的滑动也计算进去了。这个是没有必要的。见踩坑3内的代码,可以兼容滑动的情况。

踩坑3之h5获取轮播控件的坐标与宽高

没啥技术亮点,直接看代码

//获取轮播控件的宽高以及相对于原点的位置
function getViewPagerInfo() {
    var width = img.clientWidth;
    var height = img.clientHeight;
    var elem = getElementRect(img);
    //调用android代码
    window.controller.getH5ViewPagerInfo(elem.x,elem.y,width,height);
}
//获取元素的坐标
function getElementRect(e){
    var box = e.getBoundingClientRect();
    var x = box.left;
    var y = box.top;
    console.log("x::" + x);
    console.log("y::" + y);
    return {x:x , y: y};
}

踩坑4之轮播宽高坐标的获取时机

因为h5页面的高度是不确定的。很有可能是可以上下滑动的。所以我们轮播的区域也是会变化的,而且!!!轮播的区域可能不止一个,这个需要注意。轮播区域的获取时机有两个,一个是刚加载h5页面的时候,另外一个就是滑动的时候,在js代码里写

window.onload = function(){
  ...
}

window.onscroll = function(){
  ...
}

踩坑6之h5与android坐标系的转换

该需求最大的坑就在于h5与android坐标系的换算,h5的坐标系与android的坐标系的不同在于

  1. h5的坐标系以webview左上角的点为准,而android得坐标系以屏幕左上角的点为准。因此,这里要将通知栏的高度计算进去。
  2. h5的坐标系采用的是css的像素,而android是采用的设备的像素值。这两个像素需要进行换算,换算的规则也很简单,与设备的像素密度相关。

假设我们现在拿到了轮播的坐标,以及宽高,并且通过js回传给了java,,此时,我们就可以进行最重要的touch事件的判断了。

首先我们定义一个内部类用来封装轮播的宽高和坐标

 class PagerDesc {
    private int top;
    private int left;
    private int right;
    private int bottom;

    public PagerDesc(int top, int left , int right ,int bottom ) {
        this.top = top;
        this.bottom = bottom;
    }
}

考虑到目前多数的轮播都是横向充满全屏,因此这里我们只考虑touch事件在y轴上的坐标。

接下来,需要通过js将上述类创建所需要的数据回传给java用来创建对象。

  private  PagerDesc mPagerDesc;

    @JavascriptInterface
    public void getH5ViewPagerInfo(int x ,int y , int width , int height){
        mPagerDesc = new PagerDesc(y,x,x+width,y+height);
    }

最关键的一步,我们要在webview的onTouchListener进行如下处理。

        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            //获取y轴坐标
            float y = motionEvent.getRawY();
            switch (motionEvent.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    getHTMLPosition();
                    if (null != mPagerDesc) {
                        int top = mPagerDesc.top;
                        int bottom = top + (mPagerDesc.bottom - mPagerDesc.top);
                        //将css像素转换为android设备像素并考虑通知栏高度
                        top = (int) (top * metric.density) + height 
                        bottom = (int) (bottom * metric.density) + height    
                        //如果触摸点的坐标在轮播区域内,则由webview来处理事件,否则由viewpager来处理
                        if (y > top && y < bottom) {
                            webview.requestDisallowInterceptTouchEvent(true);
                        } else {
                            webview.requestDisallowInterceptTouchEvent(false);
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    break;
                case MotionEvent.ACTION_MOVE:
                    break;
            }

至此,轮播和ViewPager的滑动冲突及解决方案已经介绍完了。这个问题考察的点还是挺多的,需要开发者有一定的JS基础,需要懂得JS与java的相互调用,以及深入理解touch事件的传递及拦截机制。当然解决的过程就是踩坑与提高的过程,希望本文能给遇到该问题的小伙伴一个思路。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容