View事件分發(fā)機(jī)制(一)OnTouch事件和onTouchEvent事件

一褒繁,說(shuō)在最前面

我們知道亦鳞,View事件分發(fā)機(jī)制之所以重要是因?yàn)樗粌H可以讓我們理解MotionEvent從Activity到Window然后到ViewRoot最后傳遞到ViewGroup最后到View的整個(gè)過(guò)程中的是否攔截是否消耗以及如何處理事件的詳細(xì)細(xì)節(jié),它在開(kāi)發(fā)中更直接的好處是,他可以給我們處理滑動(dòng)沖突時(shí)提供思路并找到解決的辦法燕差。本篇重點(diǎn)放在View對(duì)事件的消耗處理OnTouch和onTouchEvent上面遭笋。

二,簡(jiǎn)單介紹整個(gè)事件分發(fā)我們會(huì)遇到最重要的三個(gè)函數(shù)

這三個(gè)函數(shù)分別是dispatchTouchEvent徒探、onInterceptTouchEvent瓦呼、和onTouchEvent。
他們的關(guān)系則為:如果能夠傳遞到當(dāng)前View那么他就會(huì)執(zhí)行dispatchTouchEvent方法测暗,然后在dispatchTouchEvent內(nèi)部會(huì)調(diào)用onInterceptionTouchEvent表示是否攔截這個(gè)事件央串,如果攔截就會(huì)嘗試進(jìn)行事件消耗。當(dāng)然消耗事件分為T(mén)ouchListener事件和TouchEvent事件碗啄,這也是在dispatchTouchEvent中完成的质和。下面會(huì)詳細(xì)介紹dispatchTouchEvent函數(shù)中的TouchListener和TouchEvent事件的處理時(shí)機(jī)。關(guān)于事件攔截細(xì)節(jié)以后會(huì)放在處理滑動(dòng)沖突時(shí)候一起介紹稚字。

三饲宿,dispatchTouchEvent

dispatchTouchEvent是處理觸摸事件分發(fā),事件(多數(shù)情況)是從Activity的dispatchTouchEvent開(kāi)始的。執(zhí)行super.dispatchTouchEvent(ev)胆描,事件向下分發(fā)瘫想。返回ture則表示已經(jīng)被消費(fèi),否則返回false昌讲。

在什么條件下會(huì)被消費(fèi)国夜?(截取部分源碼)

if (onFilterTouchEventForSecurity(event)) {
    ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnTouchListener != null && 
        (mViewFlags&ENABLED_MASK)==ENABLED && li.mOnTouchListener.onTouch(this,event)) {
            result = true;
    }

    if (!result && onTouchEvent(event)) {
        result = true;
    }
}

1,第一個(gè)消耗條件

需要注明的是:

  • li != null:ListenerInfor里面包含了幾個(gè)Listener短绸,如TouchListener车吹、TouchListener,F(xiàn)ocusChangeListener鸠按,LayoutChangeListeners,ScrollChangeListener等饶碘。
  • li.mOnTouchListener != null:mOnTouchListener是由View設(shè)置的目尖,比如mButton.setOnTouchListener()。所以如果View設(shè)置了Touch監(jiān)聽(tīng)扎运,那么mOnTouchListener不空瑟曲;反之為null。
  • (mViewFlags & ENABLED_MASK) == ENABLED:表示當(dāng)前view是否可用豪治。
  • li.mOnTouchListener.onTouch(this, event):表示onTouch是否被消耗洞拨。

只有上面四個(gè)條件同時(shí)滿足才會(huì)返回true表示消耗了此事件。

2负拟,第二個(gè)消耗條件

如果這幾個(gè)條件有一個(gè)沒(méi)有滿足烦衣,那么就看第二個(gè)if語(yǔ)句。
那么此時(shí)關(guān)鍵在于onTouchEvent這個(gè)方法,如果onTouEvent返回true花吟,那么就表示消耗了這個(gè)事件秸歧。可以看出onTouchListener的優(yōu)先級(jí)比onTouchEvent優(yōu)先級(jí)高衅澈,onTouchEvent源碼:

public boolean onTouchEvent(MotionEvent event) {
    final float x = event.getX();
    final float y = event.getY();
    final int viewFlags = mViewFlags;
    final int action = event.getAction();

    if ((viewFlags & ENABLED_MASK) == DISABLED) {
        if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
        }
        
        return (((viewFlags & CLICKABLE) == CLICKABLE
            || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
            || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
    }

    if (mTouchDelegate != null) {
        if (mTouchDelegate.onTouchEvent(event)) {
            return true;
        }
    }

    if (((viewFlags & CLICKABLE) == CLICKABLE ||
        (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
        (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
    switch (action) {
        case MotionEvent.ACTION_UP:
            boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
            if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                boolean focusTaken = false;
                if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                    focusTaken = requestFocus();
                }

                if (prepressed) {
                    setPressed(true, x, y);
                }

                if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {

                removeLongPressCallback();

                if (!focusTaken) {

                    if (mPerformClick == null) {
                    mPerformClick = new PerformClick();
                     }
                    if (!post(mPerformClick)) {
                        performClick();
                    }
            
                }

                if (mUnsetPressedState == null) {
                    mUnsetPressedState = new UnsetPressedState();
                }

                if (prepressed) {
                    postDelayed(mUnsetPressedState,
                    ViewConfiguration.getPressedStateDuration());
                } else if (!post(mUnsetPressedState)) {
                mUnsetPressedState.run();
                }

                removeTapCallback();
            }
            mIgnoreNextUpEvent = false;
            break;

        case MotionEvent.ACTION_DOWN:
            mHasPerformedLongPress = false;

            if (performButtonActionOnTouchDown(event)) {
                break;
            }

            boolean isInScrollingContainer = isInScrollingContainer();

            if (isInScrollingContainer) {
                mPrivateFlags |= PFLAG_PREPRESSED;
                if (mPendingCheckForTap == null) {
                    mPendingCheckForTap = new CheckForTap();
                }
                mPendingCheckForTap.x = event.getX();
                mPendingCheckForTap.y = event.getY();
                postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
            } else {
                setPressed(true, x, y);
                checkForLongClick(0);
            }
                break;

        case MotionEvent.ACTION_CANCEL:
            setPressed(false);
            removeTapCallback();
            removeLongPressCallback();
            mInContextButtonPress = false;
            mHasPerformedLongPress = false;
            mIgnoreNextUpEvent = false;
            break;

        case MotionEvent.ACTION_MOVE:
            drawableHotspotChanged(x, y);

            if (!pointInView(x, y, mTouchSlop)) {
                removeTapCallback();
                if ((mPrivateFlags & PFLAG_PRESSED) != 0) {

                    removeLongPressCallback();

                    setPressed(false);
                }
            }
            break;
        }

        return true;
    }

    return false;
}

第一種情況
先看第一個(gè)if語(yǔ)句键菱,如果這個(gè)view不可用,但是只要他滿足CLICKABLE或LONG_CLICKABLE或CONTEXT_CLICKABLE的就返回true今布。但是沒(méi)有處理點(diǎn)擊事件经备。


第二種情況
如果這個(gè)view可用,看第三個(gè)if語(yǔ)句部默,如果CLICKABLE或LONG_CLICKABLE或CONTEXT_CLICKABLE滿足任何一個(gè)侵蒙,那么就返回true,也就是dispatchTouchEvent返回true甩牺,消耗了這個(gè)事件蘑志。如果都不滿足就返回false,也就是沒(méi)有消耗這個(gè)事件贬派。
需要注意的是在第二種情況中當(dāng)滿足條件的時(shí)候急但,if語(yǔ)句里面判斷MotionEvent.ACTION_UP:的時(shí)候,如果滿足里面的情況會(huì)調(diào)用performClick()搞乏,也就是調(diào)用了onClick事件波桩。

3,需要注意的一些細(xì)節(jié)

  • 不同的view的clickable屬性有差別请敦。比如:Button默認(rèn)clickable屬性為true镐躲。
  • view的longClickable默認(rèn)屬性為false。
  • 我們可以通過(guò)兩種方式改變這個(gè)屬性侍筛。
  • setClickable()和setLongClickListener()
  • 設(shè)置監(jiān)聽(tīng)萤皂。如:setOnClickListener()會(huì)將View的CLICKABLE設(shè)置為true;setOnLongClickListener()會(huì)將View的LONG_CLICKABLE設(shè)置為true匣椰。

4裆熙,對(duì)于OnTouch和onTouchEvent的補(bǔ)充

上面只是對(duì)處理的時(shí)機(jī)進(jìn)行介紹,我們還需要知道的是這兩個(gè)事件的其他區(qū)別

  • ontouch他的處理粒度更小禽笑,他可以處理down入录、move和up組成的各種事件。
  • 但是onTouchEvent只是onClick監(jiān)聽(tīng)事件佳镜。

四僚稿,onInterceptTouchEvent

onInterceptTouchEvent是ViewGroup提供的方法,默認(rèn)返回false蟀伸,返回true表示攔截蚀同。

五缅刽,onTouchEvent

onTouchEvent是View中提供的方法,ViewGroup也有這個(gè)方法唤崭,view中不提供onInterceptTouchEvent拷恨。view中默認(rèn)返回true,表示消費(fèi)了這個(gè)事件谢肾。

六腕侄,View,ViewGroup和Activity里面的回調(diào)函數(shù)

1芦疏,view里面只有兩個(gè)回調(diào)函數(shù)

public boolean dispatchTouchEvent(MotionEvent ev)冕杠;    
public boolean onTouchEvent(MotionEvent ev);   

2,ViewGroup里面有三個(gè)回調(diào)函數(shù)

public boolean dispatchTouchEvent(MotionEvent ev);    
public boolean onInterceptTouchEvent(MotionEvent ev);   
public boolean onTouchEvent(MotionEvent ev);  

3,Activity中的兩個(gè)回調(diào)函數(shù)

public boolean dispatchTouchEvent(MotionEvent ev)酸茴;    
public boolean onTouchEvent(MotionEvent ev);    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末分预,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子薪捍,更是在濱河造成了極大的恐慌笼痹,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酪穿,死亡現(xiàn)場(chǎng)離奇詭異凳干,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)被济,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)救赐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人只磷,你說(shuō)我怎么就攤上這事经磅。” “怎么了钮追?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵预厌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我元媚,道長(zhǎng)轧叽,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任惠毁,我火速辦了婚禮犹芹,結(jié)果婚禮上崎页,老公的妹妹穿的比我還像新娘鞠绰。我一直安慰自己,他們只是感情好飒焦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布蜈膨。 她就那樣靜靜地躺著屿笼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪翁巍。 梳的紋絲不亂的頭發(fā)上驴一,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音灶壶,去河邊找鬼肝断。 笑死,一個(gè)胖子當(dāng)著我的面吹牛驰凛,可吹牛的內(nèi)容都是我干的胸懈。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼恰响,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼趣钱!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起胚宦,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤首有,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后枢劝,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體井联,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年呈野,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了低矮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡被冒,死狀恐怖军掂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情昨悼,我是刑警寧澤蝗锥,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站率触,受9級(jí)特大地震影響终议,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜葱蝗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一穴张、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧两曼,春花似錦皂甘、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)璧瞬。三九已至,卻和暖如春渐夸,著一層夾襖步出監(jiān)牢的瞬間嗤锉,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工墓塌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瘟忱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓苫幢,卻偏偏與公主長(zhǎng)得像酷誓,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子态坦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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