Viewpager與webview滑動(dòng)沖突的解決方案

場(chǎng)景描述

最近在接觸h5與android混合開(kāi)發(fā)時(shí)遇到一個(gè)問(wèn)題,在一個(gè)activity使用ViewPager+Fragment結(jié)構(gòu),某個(gè)Fragment包含了一個(gè)webview。而在這個(gè)webview展示的h5里有一個(gè)橫屏輪播的元素,此時(shí)當(dāng)我們橫向滑動(dòng)的時(shí)候,大多數(shù)情況下是ViewPager在滑動(dòng)(這里說(shuō)是大多數(shù)情況是不考慮網(wǎng)頁(yè)可以橫向滑動(dòng)的特殊情況)。因此我們需要判斷并處理事件。

如下圖所示,藍(lán)色線條中間的部分是一個(gè)輪播。而整個(gè)首頁(yè)的結(jié)構(gòu)是一個(gè)Viewpager。


Paste_Image.png

問(wèn)題分析

其實(shí)h5的輪播基本都會(huì)支持手滑動(dòng)事件(指JS控制輪播的滑動(dòng)),我們要做的就是判斷什么時(shí)候,touch事件由h5來(lái)處理,什么時(shí)候由ViewPager來(lái)處理,這里就不再講述android的事件機(jī)制,有興趣的同學(xué)可以去老衲之前發(fā)布的文章中去找。這里只用到了其中一個(gè)知識(shí)點(diǎn),即child如何影響parent的事件處理。所涉及到的方法就是

//當(dāng)result為true的時(shí)候,child會(huì)阻止parent獲取touch事件,反之則不會(huì)影響。
requestDisallowInterceptTouchEvent(result);

而這個(gè)方法參數(shù)result的值是true還是false,就是今天要踩得坑之一。

Paste_Image.png

主要思路

  1. 首先我們需要確定的是,需要重寫誰(shuí)的onTouch方法,webview還是viewpager,當(dāng)然是webview,通過(guò)webview來(lái)主動(dòng)控制viewpager的事件獲取權(quán)限。

  2. 接下來(lái),為了判斷,我們還需要輪播控件的坐標(biāo)和范圍,這個(gè)可以有h5和JS來(lái)實(shí)現(xiàn)

  3. 范圍有了。接下來(lái)就是要真正進(jìn)行touch的坐標(biāo)判斷了。

踩坑1之Java與JS的之間相互調(diào)用的順序

關(guān)于android內(nèi)的java方法與html內(nèi)的js方法相互調(diào)用請(qǐng)大家自行百度。這里要提醒大家的是,當(dāng)JS與java在進(jìn)行交互的時(shí)候,他們并不是同步執(zhí)行的。舉個(gè)栗子

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

當(dāng)通過(guò)上述方法調(diào)用JS的getViewPagerInfo方法時(shí),而JS的getViewPagerInfo方法內(nèi)部又調(diào)用了java的下列方法時(shí)。

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

假如我們需要按順序執(zhí)行如下兩個(gè)方法

doCheck();
showToast();

當(dāng)執(zhí)行完doCheck的loadurl方法之后,他會(huì)去執(zhí)行showToast,不會(huì)等JS回調(diào)java的getH5ViewPagerInfo方法執(zhí)行完再執(zhí)行。

踩坑2之JS滑動(dòng)

剛開(kāi)始接到這個(gè)需求的時(shí)候,會(huì)想的太多,導(dǎo)致剛開(kāi)始考慮h5的時(shí)候順帶把網(wǎng)頁(yè)的滑動(dòng)也計(jì)算進(jìn)去了。這個(gè)是沒(méi)有必要的。見(jiàn)踩坑3內(nèi)的代碼,可以兼容滑動(dòng)的情況。

踩坑3之h5獲取輪播控件的坐標(biāo)與寬高

沒(méi)啥技術(shù)亮點(diǎn),直接看代碼

//獲取輪播控件的寬高以及相對(duì)于原點(diǎn)的位置
function getViewPagerInfo() {
    var width = img.clientWidth;
    var height = img.clientHeight;
    var elem = getElementRect(img);
    //調(diào)用android代碼
    window.controller.getH5ViewPagerInfo(elem.x,elem.y,width,height);
}
//獲取元素的坐標(biāo)
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之輪播寬高坐標(biāo)的獲取時(shí)機(jī)

因?yàn)閔5頁(yè)面的高度是不確定的。很有可能是可以上下滑動(dòng)的。所以我們輪播的區(qū)域也是會(huì)變化的,而且!?。≥啿サ膮^(qū)域可能不止一個(gè),這個(gè)需要注意。輪播區(qū)域的獲取時(shí)機(jī)有兩個(gè),一個(gè)是剛加載h5頁(yè)面的時(shí)候,另外一個(gè)就是滑動(dòng)的時(shí)候,在js代碼里寫

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

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

踩坑6之h5與android坐標(biāo)系的轉(zhuǎn)換

該需求最大的坑就在于h5與android坐標(biāo)系的換算,h5的坐標(biāo)系與android的坐標(biāo)系的不同在于

  1. h5的坐標(biāo)系以webview左上角的點(diǎn)為準(zhǔn),而android得坐標(biāo)系以屏幕左上角的點(diǎn)為準(zhǔn)。因此,這里要將通知欄的高度計(jì)算進(jìn)去。
  2. h5的坐標(biāo)系采用的是css的像素,而android是采用的設(shè)備的像素值。這兩個(gè)像素需要進(jìn)行換算,換算的規(guī)則也很簡(jiǎn)單,與設(shè)備的像素密度相關(guān)。

假設(shè)我們現(xiàn)在拿到了輪播的坐標(biāo),以及寬高,并且通過(guò)js回傳給了java,,此時(shí),我們就可以進(jìn)行最重要的touch事件的判斷了。

首先我們定義一個(gè)內(nèi)部類用來(lái)封裝輪播的寬高和坐標(biāo)

 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;
    }
}

考慮到目前多數(shù)的輪播都是橫向充滿全屏,因此這里我們只考慮touch事件在y軸上的坐標(biāo)。

接下來(lái),需要通過(guò)js將上述類創(chuàng)建所需要的數(shù)據(jù)回傳給java用來(lái)創(chuàng)建對(duì)象。

  private  PagerDesc mPagerDesc;

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

最關(guān)鍵的一步,我們要在webview的onTouchListener進(jìn)行如下處理。

        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            //獲取y軸坐標(biāo)
            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像素轉(zhuǎn)換為android設(shè)備像素并考慮通知欄高度
                        top = (int) (top * metric.density) + height 
                        bottom = (int) (bottom * metric.density) + height    
                        //如果觸摸點(diǎn)的坐標(biāo)在輪播區(qū)域內(nèi),則由webview來(lái)處理事件,否則由viewpager來(lái)處理
                        if (y > top && y < bottom) {
                            webview.requestDisallowInterceptTouchEvent(true);
                        } else {
                            webview.requestDisallowInterceptTouchEvent(false);
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    break;
                case MotionEvent.ACTION_MOVE:
                    break;
            }

至此,輪播和ViewPager的滑動(dòng)沖突及解決方案已經(jīng)介紹完了。這個(gè)問(wèn)題考察的點(diǎn)還是挺多的,需要開(kāi)發(fā)者有一定的JS基礎(chǔ),需要懂得JS與java的相互調(diào)用,以及深入理解touch事件的傳遞及攔截機(jī)制。當(dāng)然解決的過(guò)程就是踩坑與提高的過(guò)程,希望本文能給遇到該問(wèn)題的小伙伴一個(gè)思路。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評(píng)論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,400評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書人閱讀 178,136評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 63,714評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,452評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 55,818評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 42,997評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,552評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,292評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,510評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,721評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 35,121評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 36,429評(píng)論 1 294
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,235評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,480評(píng)論 2 379

推薦閱讀更多精彩內(nèi)容