自定義側(cè)滑刪除帶你詳解事件分發(fā)一二

自定義側(cè)滑刪除帶你詳解事件分發(fā)一二

android中的事件分發(fā)算是我第一次詳細的了解android的某種機制,那時我是一直查博客,打印日志胜臊,來詳細的分析了每個事件分發(fā)的流程,也得益于當時學的比較認真疗杉,所以我現(xiàn)在的記憶還是很清晰。記得當時為什么會學事件分發(fā)蚕礼,就是因為我們當時的一個項目里面烟具,商品詳情頁面是模仿京東的,我是從那個控件開始詳細了解android事件分發(fā)的奠蹬。今天就通過一個非常簡單的自定義側(cè)滑刪除控件來了解事件分發(fā)的機制朝聋。

先上效果:


event_1.gif

側(cè)滑刪除大家都不陌生,實現(xiàn)的方式也是多種多樣囤躁,結(jié)合今天的主題冀痕,我們就以一個自定義的LinearLayout來實現(xiàn)側(cè)滑刪除。如果要自定義滑動相關(guān)的自定義控件狸演,就一定要對View的坐標關(guān)系有比較清晰的了解言蛇,比如調(diào)用了scrollTo(),scrollBy()后變化的是scrollX和scrollY,而left宵距,right腊尚,top,bottom满哪,x婿斥,y等坐標并沒有改變劝篷,因為根據(jù)官方文檔知道,scrollTo()和scrollBy()移動的是View的內(nèi)容受扳,而并沒有影響到View相對于父View的左上角的相對關(guān)系携龟。還有MotionEvent中的getRawX(),getRawY(),這些是相對整個屏幕左上角的坐標兔跌,至于該使用相對父View的還是使用相對的整個屏幕的左上角的就要視情況而定勘高。只要用的一套對應的就行。

我們想象一下坟桅,這個自定義View可以讓它繼承LinearLayout华望,相比于直接繼承View這樣我們就能省去很多事,省去Measure和Layout的步驟仅乓。這個時候我們就能專注于處理滑動和事件分發(fā)赖舟。學習View的事件分發(fā)一般都是用來處理滑動沖突的,并且一般都會結(jié)合Scroller和VelocityTracker夸楣。側(cè)滑刪除控件主要就是側(cè)滑嗎宾抓,我們想辦法把這個LinearLayout滑動起來就好了,這樣就把原先看不到View顯示出來了豫喧。好了基本的思路就是這樣石洗,很簡單。那么下面就來看一下事件分發(fā)吧紧显。

android中事件分發(fā)我們一般只考慮View和ViewGroup就可以了讲衫,先說一下大概的流程,首先事件分發(fā)一定會先傳到Activity孵班,然后這個Activity會持有一個Window涉兽,這時就會把這個事件傳遞給相應的Window,這個Window里面有一個DecorView篙程,這個時候這個事件就傳遞到了View層枷畏。一個事件傳遞到ViewGroup首先會進入DispatchEvent方法,這個時候首先會有一個標志位虱饿,這個標志位就是這個DispatchEvent的返回值拥诡,用來告訴上一層的View是否處理這個MotionEvent。首先這個標志位會先置為false郭厌,然后問這個ViewGroup是否需要攔截這個MotionEvent袋倔,這個時候就是進入了onInterceptEvent(),這個函數(shù)只有在ViewGroup內(nèi)會被調(diào)用,這個函數(shù)的返回值就是告訴這個ViewGroup是否把這個事件向下傳折柠,自己不處理宾娜。還是選擇自己處理不再向下傳。這個函數(shù)返回false的話扇售,就表示不攔截這個事件前塔,會把這個事件向下傳遞嚣艇。如果返回的是true,這個時候這個ViewGroup就會變成了View华弓,表示它自己會處理這個事件食零。不再向下傳遞。當一個事件傳遞到View的時候寂屏,首先會進入dispatchEvent()方法贰谣,然后會進入onTouchEvent()方法,在onTouchEvenet里面會調(diào)用onTouch和onClick方法迁霎,前提是你設置了這兩個方法吱抚。

好了,有了上面的基礎我們就開始動手吧考廉,側(cè)滑刪除嗎秘豹,我就讓這個LinearLayout滑動就行了。首先會判斷在什么時候我需要攔截這個事件昌粤,應該就是當用戶是在左右滑動的時候既绕,我們會認為用戶是想使用側(cè)滑刪除的功能,那么這時候應該攔截這個事件涮坐,也就是讓我們的LinearLayout來處理凄贩。

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    int action = ev.getAction();
    boolean intercepted = false;
    int x = (int) ev.getX();
    if (!mScroller.isFinished()) {
        mScroller.abortAnimation();
        return true;
    }
    switch (action) {
        case MotionEvent.ACTION_DOWN:
            mLastX = (int) ev.getX();
            intercepted = false;
            break;
        case MotionEvent.ACTION_MOVE:
            if (Math.abs(x - mLastX) > mTouchSlop) intercepted = true;
            else intercepted = false;
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            intercepted = false;

            break;
    }
    mLastX = x;
    return intercepted;
}

mTouchSlop:是系統(tǒng)認定是滑動的最小值。
mScroller:是用來處理滑動的膊升,用這個處理滑動會有一個動畫的效果怎炊,沒那么突然,雖然它的內(nèi)部實現(xiàn)不是動畫廓译。
    可以看到在這個方法里面只有當用戶在X軸方向的滑動分距離大于系統(tǒng)認為的最小滑動的 時候才會攔截事件评肆。事件攔截寫來以后就會交給onTouchEvent處理。那么這時我們就會在這個函數(shù)處理相關(guān)的滑動非区。



@Override
public boolean onTouchEvent(MotionEvent event) {

    getParent().requestDisallowInterceptTouchEvent(false);
    if (!mScroller.isFinished()) {
        mScroller.abortAnimation();
        return true;
    }
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mLastX = (int) event.getX();
            break;
        case MotionEvent.ACTION_MOVE:
            int dx = (int) (event.getX() - mLastX);
            mLastX = (int) event.getX();
            scrollBy(-dx, 0);
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            int scrollX = getScrollX();
            if (scrollX > MAXWIDTH / 3) {
                mScroller.startScroll(scrollX, 0, MAXWIDTH - scrollX, 0, 200);
            } else {
                mScroller.startScroll(scrollX, 0, -scrollX, 0, 200);
            }
            invalidate();
            break;
    }
    mLastX = (int) event.getX();
    return true;
}

    mScroller.startScroll(scrollX, 0, -scrollX, 0, 2000);這個函數(shù)表示
的意思就是從(scrollX瓜挽,0)點滑動到(scrollX + (-scrollX),0 + 0)點,并且
耗費的時間是200ms征绸,如果使用Scroller來處理滑動就一定需要重寫
computeScroll()久橙,并且一定別忘記了postInvalidate(),因為Scroller只是負
責計算在相應的時間這個x應該變化到什么值管怠。它只是這個功能淆衷,所以我們拿到這個值后需要自己去scrollTo,并且需要調(diào)用 postInvalidate();這樣他才會繼續(xù)調(diào)用
computeScroll()來完成滑動渤弛,這樣不停的重新繪制祝拯,顯示出來的就是動畫效果。


 @Override
public void computeScroll() {
    super.computeScroll();
    if (mScroller.computeScrollOffset()) {
        scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
        postInvalidate();
    }
}

上面就算側(cè)滑刪除的功能實現(xiàn)了,可以看出非常簡單佳头,整個文件就一百多行代碼鹰贵,項目非常簡單,但是也能體現(xiàn)我們對事件分發(fā)各個環(huán)節(jié)的掌握康嘉,把對的代碼寫在對的地方碉输。

下面總結(jié)一下事件分發(fā)的知識點

1.同一個事件序列是指從手指接觸屏幕的那一刻起到手指離開屏幕的那一刻結(jié)束,在這個過程中所產(chǎn)生的一系列事件亭珍,這個事件序列以DOWN事件開始敷钾,中間含有數(shù)量不定的MOVE事件,最終以UP事件結(jié)束块蚌。

2.正常情況下闰非,一個事件序列只能被一個View攔截且消耗膘格,因為一個元素攔截了某次事件那么同一個事件序列內(nèi)的所有事件都會直接交給它處理峭范。當然不包括父View從中間攔截。

3.某個View決定攔截瘪贱,那么這一個事件序列都只能由它來處理(如果事件序列能夠傳遞給它的話)纱控,并且它的onInterceptTouchEvent不會再被調(diào)用,這個很好理解菜秦,就是說甜害,當一個View決定攔截一個事件后,那么系統(tǒng)就會把同一個事件序列內(nèi)的其他方法都直接交給他來處理球昨。因此就不用再調(diào)用這個View的onInterceptTouchEvent去詢問它是否要攔截了尔店。

4.某個View一旦開始處理事件,如果它不消耗DOWN事件主慰,(onTouchEvent返回了fasle)嚣州,那么同一事件序列中的其他事件都不會再交給它處理,并且事件將重新交由它的父元素去處理共螺。即父元素的onTouchEvent會被調(diào)用该肴。

5.如果View不消耗除DOWN以外的其他事件,那么這個點擊事件會消失藐不,此時父元素的onTouchEvent并不會被調(diào)用匀哄,并且當前View可以持續(xù)收到后續(xù)的事件追蹤這些消失的點擊事件會傳遞給Activity處理。

6.ViewGroup默認不攔截任何事件雏蛮,android源碼中ViewGroup的onInterceptTouchEvent方法默認返回false涎嚼。

7.View沒有onInterceptTouchEvent方法,一旦有點擊事件傳遞給它挑秉,那么它的onTouchEvent方法就會被調(diào)用法梯。

8.View的onTouchEvent默認都會消耗事件(返回true),除非它是不可點擊的(clickable和longClickable同時為false)衷模,View的longClickable屬性默認都為false鹊汛,clickable屬性要分情況蒲赂,比如Button的clickable屬性默認為true,而TextView的clickable屬性默認為false刁憋。

9.View的enable屬性不影響onTouchEvent的默認返回值滥嘴,哪怕一個View是disable狀態(tài)的,只要它的clickable或者longclickable又一個為true至耻,那么它的onTouchEvent就返回true若皱。

10.onClick會發(fā)生的前提是當前View是可點擊的,并且他收到了down和up的事件尘颓。

11.事件傳遞過程是由外向內(nèi)的走触,即事件總是先傳遞給父元素,然后再由父元素分發(fā)子View疤苹,通過requestDisAllowInterceptTouchEvent方法可以再子元素中干預父元素的事件分發(fā)過程互广。當時DOWN事件除外。

12.CANCEL事件卧土,官方文檔講的是當前手勢被釋放惫皱,你將不會接收到其他事件,應該像UP一樣對待它尤莺,那到底什么情況會觸發(fā)這個事件呢旅敷?當前控件(子控件)收到前驅(qū)事件(DOWN或者MOVE)后,它的父控件突然插手攔截了事件的傳遞颤霎,這時當前控件就會收到CANCEL媳谁,收到此事件后,不管子控件此時返回true或者false友酱,都認為這一個動作已經(jīng)完成晴音,不會再回傳到父控件的onTouchEvent中處理,同時后續(xù)事件會通過dispatchEvent方法直接傳遞到父控件這里來處理粹污。

13.父控件不攔截DOWN事件段多,如果子控件的onTouchEvent返回了true,在MOVE時父控件攔截壮吩,此時CANCEL會傳遞到子控件进苍,并且子控件只會收到這一個CANCEL事件,后續(xù)的事件序列都會進入到父控件的onTouchEvent鸭叙,且不再走父控件的onInterceptTouchEvent方法觉啊,因為此時父控件相當于View,mTarget為空時直接進入onTouchEvent方法沈贝,所以此時在onTouchEvent可以監(jiān)聽到后續(xù)的事件是因為沒有控件會處理這個事件杠人。如果父控件的onTouchEvent返回false,此時會上傳到父控件的父控件,最終Activity會處理嗡善。如果父控件的onTouchEvent返回true表明后續(xù)事件會由這個父控件來處理辑莫。

14.最后一點時觸摸區(qū)域的問題,如果觸摸區(qū)域在子控件內(nèi)罩引,同時父控件沒有攔截事件傳遞各吨,則不管子控件是否攔截此事件都會傳遞到子控件的onTouchEvent中處理,可以看成一種責任吧袁铐,因為我點的就是你揭蜒,你父親沒有攔截說明它不想處理了,那就會傳遞到你這里剔桨。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屉更,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子洒缀,更是在濱河造成了極大的恐慌瑰谜,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帝洪,死亡現(xiàn)場離奇詭異似舵,居然都是意外死亡,警方通過查閱死者的電腦和手機葱峡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來龙助,“玉大人砰奕,你說我怎么就攤上這事√崮瘢” “怎么了军援?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長称勋。 經(jīng)常有香客問我胸哥,道長,這世上最難降的妖魔是什么赡鲜? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任空厌,我火速辦了婚禮,結(jié)果婚禮上银酬,老公的妹妹穿的比我還像新娘嘲更。我一直安慰自己,他們只是感情好揩瞪,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布赋朦。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宠哄。 梳的紋絲不亂的頭發(fā)上壹将,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天,我揣著相機與錄音毛嫉,去河邊找鬼瞭恰。 笑死,一個胖子當著我的面吹牛狱庇,可吹牛的內(nèi)容都是我干的惊畏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼密任,長吁一口氣:“原來是場噩夢啊……” “哼颜启!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起浪讳,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤缰盏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后淹遵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體口猜,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年透揣,在試婚紗的時候發(fā)現(xiàn)自己被綠了济炎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡辐真,死狀恐怖须尚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情侍咱,我是刑警寧澤耐床,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站楔脯,受9級特大地震影響撩轰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜昧廷,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一堪嫂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧麸粮,春花似錦溉苛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽娇唯。三九已至,卻和暖如春寂玲,著一層夾襖步出監(jiān)牢的瞬間塔插,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工拓哟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留想许,地道東北人。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓断序,卻偏偏與公主長得像流纹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子违诗,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,129評論 25 707
  • 介紹自己負責的部分漱凝,如何實現(xiàn)的。 框架的搭建排查問題以及結(jié)解決方式兼容性保證性能優(yōu)化上線之后模塊導致crash的比...
    黃海佳閱讀 13,184評論 6 350
  • 還有七小時 阿波羅的流矢將飛揚著 刺破夜的華緞 做夢的少年將睜開雙眼 沉醉夢鄉(xiāng)诸迟,哪能長遠茸炒? 何不讓他,煙消云散 還...
    不思中州晚閱讀 209評論 0 2
  • 一位網(wǎng)友問到:「閉包(Closure)這東西,到底有何用處绅项,在Swift裡我們會在哪些場合用到紊册?」 這問題,著實令...
    alston_tsao閱讀 2,883評論 3 1
  • Hola趁怔!作為一個喜歡給大家普及和安利冷門旅游點的大齡業(yè)余旅行者湿硝,在2017年七月份的公路旅行中,我的目的地是一個...
    JN地球游記閱讀 1,196評論 0 0