滑動(dòng)事件沖突解決方法解析

本篇文章主要是繼之前的文章 事件分發(fā) 繼續(xù)以實(shí)戰(zhàn)的角度來解決我們?nèi)粘i_發(fā)中遇到的滑動(dòng)沖突的問題。

滑動(dòng)沖突

一、前言

????滑動(dòng)沖突在我們?nèi)粘?Android 開發(fā)中非常常見,當(dāng)我們單獨(dú)使用滾動(dòng)的滑動(dòng)控件(ScrollView 、ListView、ViewPager、RecyclView )時(shí)不會(huì)發(fā)生什么問題,但當(dāng)我們將他們結(jié)合起來時(shí),就會(huì)出現(xiàn)所謂的滑動(dòng)沖突問題。

二、滑動(dòng)沖突分類

滑動(dòng)沖突一般有三種場(chǎng)景:

  • 外部滑動(dòng)與內(nèi)部滑動(dòng)方向不一致(比如 ViewPager 嵌套 ListView 時(shí))
  • 外部滑動(dòng)與內(nèi)部滑動(dòng)方向 一致 (ScrollView 嵌套 ScrollView)
  • 多層嵌套滑動(dòng)(就是將前面的兩種嵌套滑動(dòng)結(jié)合起來)

三、滑動(dòng)沖突處理思路

3.1、內(nèi)外滑動(dòng)方向不一致時(shí)處理思路

????這一類場(chǎng)景其實(shí)比較容易分析,因?yàn)橥鈱雍蛢?nèi)層滑動(dòng)的方向不一致,所以根據(jù)手勢(shì)的動(dòng)向來確定把事件給誰。默認(rèn)情況下,當(dāng)點(diǎn)擊內(nèi)層控件時(shí),事件會(huì)先一層層從外層傳到內(nèi)層,由內(nèi)層來處理。這里以外層為左右滑動(dòng),內(nèi)層為上下滑動(dòng)為例。當(dāng)判定手勢(shì)的滑動(dòng)為左右時(shí),需要外層來消費(fèi)事件,所以外層將事件攔截,即在外層的onInterceptTouchEvent中檢測(cè)為ACTION_MOVE時(shí)返回true;而如果判定手勢(shì)的滑動(dòng)為上下時(shí),需要內(nèi)層來消費(fèi)事件,外層不需要攔截,事件會(huì)傳遞到內(nèi)層來處理(具體的代碼實(shí)現(xiàn),在后面會(huì)詳細(xì)列出)。這樣就通過判斷滑動(dòng)的方向來決定事件的處理對(duì)象,從而解決滑動(dòng)沖突的問題。

????那么,如何來判定手勢(shì)的滑動(dòng)方向呢?最常用的辦法就是比較水平和豎直方向上的位移值來判斷。 MotionEvent 事件包含了事件的坐標(biāo),只要記錄一次移動(dòng)事件的起點(diǎn)和終點(diǎn)坐標(biāo),如下圖所示,通過比較在水平方向的位移|dx|和|dy|的大小,來決定滑動(dòng)的方向:|dy|>|dx|,本次移動(dòng)的方向認(rèn)為是豎直方向;反之,則認(rèn)為是水平方向。當(dāng)然,還可以通過夾角α的大小、斜率、速率等方式來作為判斷條件。
判斷水平滑動(dòng)還是垂直滑動(dòng)
3.2、內(nèi)外滑動(dòng)方向一致時(shí)處理思路

????這種場(chǎng)景要比上面一種復(fù)雜一些,因?yàn)榛瑒?dòng)方向一致,所以無法通過上述的方式來判斷將事件交給誰處理。在這種情況下,往往需要根據(jù)業(yè)務(wù)的需要來判定誰來處理事件。
????比如豎直方向的 ScrollView 嵌套 ScrollView 的場(chǎng)景下:
????手指在內(nèi)層 ScrollView 上下滑動(dòng)時(shí):事件需要被內(nèi)層 ScrollView 攔截,由內(nèi)層ScrollView 來消費(fèi);
????當(dāng)手指在外層 ScrollView 滑動(dòng)時(shí)事件需要被外層ScrollView 攔截,由外層 ScrollView來消費(fèi)。

3.3、多層滑動(dòng)嵌套時(shí)處理思路

????這種場(chǎng)景下看起來比較復(fù)雜,但其實(shí)是由前面兩種場(chǎng)景嵌套形成的。所以這種場(chǎng)景的處理方式,就是將其拆分為簡(jiǎn)單的場(chǎng)景,然后按照前面的場(chǎng)景分析方式來處理。

四、滑動(dòng)沖突兩種處理方法

4.1、外部攔截法

????顧名思義,就是在外部滑動(dòng)控件中處理攔截邏輯。這需要外部控件重寫父類的onInterceptTouchEvent 方法,在其中判斷什么時(shí)候需要攔截事件由自身處理,什么時(shí)候需要放行將事件傳給內(nèi)層控件處理,內(nèi)部控件不需要做任何處理。偽代碼如下:

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                if (父容器需要自己處理改事件) {
                    return true;//攔截
                } else {
                    return false;//不攔截
                }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }
4.2、內(nèi)部攔截法

????顧名思義,就是將事件是否需要攔截的邏輯,放到內(nèi)層控件中來處理。這種方式需要結(jié)合 requestDisllowInterceptTouchEvent(boolean) 這個(gè)方法,在內(nèi)層控件的重寫方法dispatchTouchEvent中,根據(jù)邏輯來決定外層控件何時(shí)需要攔截事件,何時(shí)需要放行。(requestDisllowInterceptTouchEvent 這個(gè)方法的作用是-是否允許外層控件攔截事件)偽代碼如下:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    switch (ev.getAction()){
        case MotionEvent.ACTION_DOWN:
            getParent().requestDisallowInterceptTouchEvent(true);
            break;
        case MotionEvent.ACTION_MOVE:
            if (父容器需要處理改事件) {
                //允許外層控件攔截事件
                getParent().requestDisallowInterceptTouchEvent(false);
            } else {
                //需要內(nèi)部控件處理該事件,不允許上層viewGroup攔截
                getParent().requestDisallowInterceptTouchEvent(true);
            }
            break;
        case MotionEvent.ACTION_UP:
            break;
        default:
            break;
    }
    return super.dispatchTouchEvent(ev);
}

同時(shí),在這種方法下需要在外層控件做下處理:

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        return false;
    } else {
        return true;
    }
}

ACTION_DOWN事件仍然不能攔截,這是因?yàn)樵?ACTION_DOWN 事件時(shí)會(huì)初始化一些狀態(tài)和標(biāo)志位等變量導(dǎo)致 requestDisllowInterceptTouchEvent(boolean) 方法會(huì)失效。因此,在外層控件的 ACTION_DOWN 事件不能攔截。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 敘述 滑動(dòng)沖突可以說是日常開發(fā)中比較常見的一類問題,也是比較讓人頭疼的一類問題,尤其是在使用第三方框架的時(shí)候,兩個(gè)...
    IAM四十二閱讀 27,401評(píng)論 8 107
  • 之前的一遍學(xué)習(xí)筆記主要就Android滑動(dòng)沖突中,在不同方向的滑動(dòng)所造成沖突進(jìn)行了了解,這種沖突很容易理解,當(dāng)然也...
    IAM四十二閱讀 4,238評(píng)論 2 50
  • 1.Android滑動(dòng)事件沖突解決辦法 滑動(dòng)事件的沖突包括兩種情形: 不同方向的滑動(dòng)沖突:比如ScrollView...
    ModestStorm閱讀 1,427評(píng)論 0 2
  • 推薦指數(shù): 6.0 書籍主旨關(guān)鍵詞:特權(quán)、焦點(diǎn)、注意力、語言聯(lián)想、情景聯(lián)想 觀點(diǎn): 1.統(tǒng)計(jì)學(xué)現(xiàn)在叫數(shù)據(jù)分析,社會(huì)...
    Jenaral閱讀 5,982評(píng)論 0 5
  • 城空了,有樹長(zhǎng)出來 我的城死了 鑄起它的人,殺死它的人 不愿因?yàn)檫@件事而驕傲 一座城的終結(jié) 永遠(yuǎn)因?yàn)榻K結(jié)這件事而顯...
    于十六閱讀 3,118評(píng)論 6 17

友情鏈接更多精彩內(nèi)容