ViewGroup 和 View 事件傳遞及處理小談,viewgroupview
前言
在自訂群組件的時候少不了會去處理一些事件相關的東西,關於事件這塊網上有很多文章,有說的對的也有說的不對的,我在理解的時候也有過一段時間的迷惑,現在把自己理解的東西寫下來,給有相同疑問的朋友提供些思路。
事件呢,分為兩個大的鏈條,一個是事件分發鏈條,一個是事件處理鏈條。分發鏈條是由外至內,也就是從父到子,父節點先收到並有權決定是否繼續分發;而事件處理鏈條則是由內至外,即先由最內部的子節點處理事件,子節點有權決定是由自己來處理事件,還是拋給父節點。
概念解釋
onTouchEvent
Implement this method to handle touch screen motion events.
此介面是用來處理事件的,如果返回true則代表已處理,返回false則代表未處理
onInterceptTouchEvent
Implement this method to intercept all touch screen motion events。
此介面是用來攔截事件的,如果返回true則代表已攔截,那麼就不會再向子物件傳遞(本身的onTouchEvent函數依然可以處理),返回false,代表未攔截。
This allows you to watch events as they are dispatched to your children, and take ownership of the current gesture at any point.
此介面允許你監控事件分發給子物件的過程,並且可以在任何點上擷取此事件的所有權。
Using this function takes some care, as it has a fairly complicated interaction with View.onTouchEvent(MotionEvent), and using it requires implementing that method as well as this one in the correct way. Events will be received in the following order:
用這個功能的時候需要小心一點兒,因為這個對View.onTouchEvent(MotionEvent)有適當的影響。並且用他的時候需要實View.onTouchEvent(MotionEvent),中的正確的方法。事件按照下面的順序被擷取:
1、You will receive the down event here
擷取down事件
2、The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle;
這個down事件有可能會被這個Group中的任何一個子物件處理掉,或者被自身的onTouchEvent() 方法處理掉。
this means you should implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it)
這就意味著你必須實現onTouchEvent()方法並且返回true,只有這樣你才能夠看到其餘的手勢動作(而不是找一個父view來處理他)
Also, by returning true from onTouchEvent(), you will not receive any following events in onInterceptTouchEvent() and all touch processing must happen in onTouchEvent() like normal.
同時,如果 onTouchEvent()返回為true,你也不會接收到 onInterceptTouchEvent()中其餘的事件並且所有的事件處理必須像onTouchEvent() 一樣正常處理。
3、For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and then to the target's onTouchEvent().
隨著你返回了false,那麼接下來的事件(up事件)會被先傳到這個介面裡,然後傳給它的 onTouchEvent().
4、If you return true from here, you will not receive any following events: the target view will receive the same event but with the action ACTION_CANCEL, and all further events will be delivered to your onTouchEvent() method and no longer appear here.
此時如果你返回了true,你就不會再接收到其餘的事件,目標view也會接收除了ACTION_CANCEL外也和它一樣的事件。其餘的事件會被傳遞到onTouchEvent() 方法中,並不會出現在此介面中。
事件關係
以上兩個事件介面的解釋內容是從api中翻譯下來的。接下來就看ViewGroup和View的事件傳遞關係
1、ViewGroup和View都有onTouchuEvent事件
2、ViewGroup有onInterceptTouchEvent事件。
通過上面概念的分析我們知道了,onInterceptTouchEvent使用來控制事件傳遞的,是從父物件向子物件傳遞。onTouchuEvent是進行事件處理,它是由子物件向父物件進行冒泡,也就是說如果子物件處理了該事件(返回true),那麼就不在向父物件傳遞了,父物件的onTouchEvent就收到down事件(以及以後的事件)了,
如果子物件處理不了該事件(返回false)那麼該事件就會冒泡給父物件的onTouchEvent,如果此時父物件處理了(返回true),那麼子物件也就無法處理以後的事件了;由onInterceptTouchEvent來決定是否對該事件進行攔截,如果事件在此處被攔截了,那麼onTouchEvent就沒有機會再去處理了。
比如,有以下的布局,並且在view上進行了一次點擊。
<cn.bitlove.test.CLayout android:id="@+id/layout" > <cn.bitlove.test.CView android:id="@+id/view" /></cn.bitlove.test.CLayout>
那麼down事件首先被Layout的onInterceptTouchEvent事件接收,如果攔截(返回true)了,那麼子物件的onTouchuEvent不會收到任何接下來的事件了,但是Layout自身的onTouchEvent依然可以處理;
如果未攔截,那麼子物件的onTouchEvent就可以處理了,如果子物件的onTouchEvent函數處理的事件(返回true),那麼父物件的onTouchEvent就沒有機會再處理了。
如果子物件的onTouchEvent對象沒有處理該事件(返回false),那麼將交由父物件進行處理,之後的事件子物件無法控制。之後如果父物件處理了down事件,那麼由父物件繼續處理後續事件,如果父物件也沒有處理這個down事件,那麼繼續向他的父物件繼續冒泡該事件,如果都沒有處理,那麼整個事件鏈結束。
總結
可以這麼說,touch事件事件鏈(donw->move->up),他由兩個過程組成,一是事件的分發,二是事件的處理。先處理事件的分發,這個分發主要影響子物件,如果事件的分發被打斷了,子物件自然沒有後續的處理了。
事件的處理,是由子物件向父物件冒泡,誰從哪個事件點接手處理,誰就接手之後的整個事件鏈條,當然他也可以處理一些環節後,繼續把後續的事件鏈交由父物件處理。需要注意的是,一旦事件鏈交出去後就沒有機會再處理了,比如子物件處理完down事件後不想處理move事件,把move事件交由父物件處理, 等父物件處理完再由自己繼續處理up事件,這樣是不行的。事件鏈控制權總是向父物件冒泡,不能往回。
後記
把握住事件的兩個鏈條,那麼對事件的理解就清晰很多了