1. 程式人生 > >Android中View和ViewGroup事件分發攔截機制完美分析

Android中View和ViewGroup事件分發攔截機制完美分析

出自:http://www.cnblogs.com/linjzong/p/4191891.html

Touch事件分發中只有兩個主角:ViewGroup和View。Activity的Touch事件事實上是呼叫它內部的ViewGroup的Touch事件,可以直接當成ViewGroup處理。

View在ViewGroup內,ViewGroup也可以在其他ViewGroup內,這時候把內部的ViewGroup當成View來分析。

ViewGroup的相關事件有三個:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。View的相關事件只有兩個:dispatchTouchEvent、onTouchEvent。

先分析ViewGroup的處理流程:首先得有個結構模型概念:ViewGroup和View組成了一棵樹形結構,最頂層為Activity的ViewGroup,下面有若干的ViewGroup節點,每個節點之下又有若干的ViewGroup節點或者View節點,依次類推。如圖:

當一個Touch事件(觸控事件為例)到達根節點,即Acitivty的ViewGroup時,它會依次下發,下發的過程是呼叫子View(ViewGroup)的dispatchTouchEvent方法實現的。簡單來說,就是ViewGroup遍歷它包含著的子View,呼叫每個View的dispatchTouchEvent方法,而當子View為ViewGroup時,又會通過呼叫ViwGroup的dispatchTouchEvent方法繼續呼叫其內部的View的dispatchTouchEvent方法。上述例子中的訊息下發順序是這樣的:①-②-⑤-⑥-⑦-③-④。dispatchTouchEvent方法只負責事件的分發,它擁有boolean型別的返回值,當返回為true時,順序下發會中斷。在上述例子中如果⑤的dispatchTouchEvent返回結果為true,那麼⑥-⑦-③-④將都接收不到本次Touch事件。來個簡單版的程式碼加深理解:

複製程式碼
 /**
     * ViewGroup
     * @param ev
     * @return
     */
    public boolean dispatchTouchEvent(MotionEvent ev){
        ....//其他處理,在此不管
        View[] views=getChildView();
        for(int i=0;i<views.length;i++){
//判斷下Touch到螢幕上的點在該子View上面 
if(...){
if(views[i].dispatchTouchEvent(ev))
return true;
} } ...
//其他處理,在此不管 } /** * View * @param ev * @return */ public boolean dispatchTouchEvent(MotionEvent ev){ ....//其他處理,在此不管 return false; }
複製程式碼

在此可以看出,ViewGroup的dispatchTouchEvent是真正在執行“分發”工作,而View的dispatchTouchEvent方法,並不執行分發工作,或者說它分發的物件就是自己,決定是否把touch事件交給自己處理,而處理的方法,便是onTouchEvent事件,事實上子View的dispatchTouchEvent方法真正執行的程式碼是這樣的

複製程式碼
/**
     * View
     * @param ev
     * @return
     */
    public boolean dispatchTouchEvent(MotionEvent ev){
        ....//其他處理,在此不管
        return onTouchEvent(event);
    }
複製程式碼

一般情況下,我們不該在普通View內重寫dispatchTouchEvent方法,因為它並不執行分發邏輯。當Touch事件到達View時,我們該做的就是是否在onTouchEvent事件中處理它。

那麼,ViewGroup的onTouchEvent事件是什麼時候處理的呢?當ViewGroup所有的子View都返回false時,onTouchEvent事件便會執行。由於ViewGroup是繼承於View的,它其實也是通過呼叫View的dispatchTouchEvent方法來執行onTouchEvent事件。

在目前的情況看來,似乎只要我們把所有的onTouchEvent都返回false,就能保證所有的子控制元件都響應本次Touch事件了。但必須要說明的是,這裡的Touch事件,只限於Acition_Down事件,即觸控按下事件,而Aciton_UP和Action_MOVE卻不會執行。事實上,一次完整的Touch事件,應該是由一個Down、一個Up和若干個Move組成的。Down方式通過dispatchTouchEvent分發,分發的目的是為了找到真正需要處理完整Touch請求的View。當某個View或者ViewGroup的onTouchEvent事件返回true時,便表示它是真正要處理這次請求的View,之後的Aciton_UP和Action_MOVE將由它處理。當所有子View的onTouchEvent都返回false時,這次的Touch請求就由根ViewGroup,即Activity自己處理了。

看看改進後的ViewGroup的dispatchTouchEvent方法

複製程式碼
View mTarget=null;//儲存捕獲Touch事件處理的View
    public boolean dispatchTouchEvent(MotionEvent ev) {

        //....其他處理,在此不管
        
        if(ev.getAction()==KeyEvent.ACTION_DOWN){
            //每次Down事件,都置為Null

if(!onInterceptTouchEvent()){
mTarget=null; View[] views=getChildView(); for(int i=0;i<views.length;i++){ if(views[i].dispatchTouchEvent(ev)) mTarget=views[i]; return true; }
} }
//當子View沒有捕獲down事件時,ViewGroup自身處理。這裡處理的Touch事件包含Down、Up和Move if(mTarget==null){ return super.dispatchTouchEvent(ev); } //...其他處理,在此不管 if(onInterceptTouchEvent()){
         //...其他處理,在此不管    
}
//這一步在Action_Down中是不會執行到的,只有Move和UP才會執行到。
        return mTarget.dispatchTouchEvent(ev);

    }
複製程式碼

ViewGroup還有個onInterceptTouchEvent,看名字便知道這是個攔截事件。這個攔截事件需要分兩種情況來說明:

1.假如我們在某個ViewGroup的onInterceptTouchEvent中,將Action為Down的Touch事件返回true,那便表示將該ViewGroup的所有下發操作攔截掉,這種情況下,mTarget會一直為null,因為mTarget是在Down事件中賦值的。由於mTarge為null,該ViewGroup的onTouchEvent事件被執行。這種情況下可以把這個ViewGroup直接當成View來對待。

2.假如我們在某個ViewGroup的onInterceptTouchEvent中,將Acion為Down的Touch事件都返回false,其他的都返回True,這種情況下,Down事件能正常分發,若子View都返回false,那mTarget還是為空,無影響。若某個子View返回了true,mTarget被賦值了,在Action_Move和Aciton_UP分發到該ViewGroup時,便會給mTarget分發一個Action_Delete的MotionEvent,同時清空mTarget的值,使得接下去的Action_Move(如果上一個操作不是UP)將由ViewGroup的onTouchEvent處理。

情況一用到的比較多,情況二個人還未找到使用場景。

從頭到尾總結一下:

1.Touch事件分發中只有兩個主角:ViewGroup和View。ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三個相關事件。View包含dispatchTouchEvent、onTouchEvent兩個相關事件。其中ViewGroup又繼承於View。

2.ViewGroup和View組成了一個樹狀結構,根節點為Activity內部包含的一個ViwGroup。

3.觸控事件由Action_Down、Action_Move、Aciton_UP組成,其中一次完整的觸控事件中,Down和Up都只有一個,Move有若干個,可以為0個。

4.當Acitivty接收到Touch事件時,將遍歷子View進行Down事件的分發。ViewGroup的遍歷可以看成是遞迴的。分發的目的是為了找到真正要處理本次完整觸控事件的View,這個View會在onTouchuEvent結果返回true。

5.當某個子View返回true時,會中止Down事件的分發,同時在ViewGroup中記錄該子View。接下去的Move和Up事件將由該子View直接進行處理。由於子View是儲存在ViewGroup中的,多層ViewGroup的節點結構時,上級ViewGroup儲存的會是真實處理事件的View所在的ViewGroup物件:如ViewGroup0-ViewGroup1-TextView的結構中,TextView返回了true,它將被儲存在ViewGroup1中,而ViewGroup1也會返回true,被儲存在ViewGroup0中。當Move和UP事件來時,會先從ViewGroup0傳遞至ViewGroup1,再由ViewGroup1傳遞至TextView。

6.當ViewGroup中所有子View都不捕獲Down事件時,將觸發ViewGroup自身的onTouch事件。觸發的方式是呼叫super.dispatchTouchEvent函式,即父類View的dispatchTouchEvent方法。在所有子View都不處理的情況下,觸發Acitivity的onTouchEvent方法。

7.onInterceptTouchEvent有兩個作用:1.攔截Down事件的分發。2.中止Up和Move事件向目標View傳遞,使得目標View所在的ViewGroup捕獲Up和Move事件。

另外,上文所列出的程式碼並非真正的原始碼,只是概括了原始碼在事件分發處理中的核心處理流程,真正原始碼各位可以自己去看,包含了更豐富的內容。

 補充:

“觸控事件由Action_Down、Action_Move、Aciton_UP組成,其中一次完整的觸控事件中,Down和Up都只有一個,Move有若干個,可以為0個。”,這裡補充下其實UP事件是可能為0個的。

最近剛好在做一個手勢放大縮小移動圖片的Demo,對此有了更多的理解。對於onInterceptTouchEvent事件,它的應用場景在很多帶scroll效果的ViewGroup中都有體現。設想一下再一個ViewPager中,每個Item都是個ImageView,我們需要對這些ImageView做Matrix操作,這不可避免要捕獲掉Touch事件,但是我們又需要做到不影響ViewPager翻頁效果,這又必須保證ViewPager能捕獲到Move事件,於是,ViewPager的onInterceptTouchEvent會對Move事件做一個過濾,當適當條件的Move事件(持續若干事件或移動若干距離,這裡我沒讀原始碼只是猜測)觸發時,並會攔截掉,返回子View一個Action_Cancel事件。這個時候子View就沒有Up事件了,很多需要在Up中處理的事物要轉到Cancel中處理。

相關推薦

AndroidViewViewGroup事件分發攔截機制完美分析

出自:http://www.cnblogs.com/linjzong/p/4191891.html Touch事件分發中只有兩個主角:ViewGroup和View。Activity的Touch事件事實上是呼叫它內部的ViewGroup的Touch事件,可以直接當成Vie

AndroidViewViewGroup的詳細解釋:

Android中View和ViewGroup的詳細解釋 1. 概述: Android系統中的所有UI類都是建立在View和ViewGroup這兩個類的基礎上的。所有View的子類成為”Widget”,所有ViewGroup的子類成為”Layout

Android ViewAndroidView對觸控事件的處理傳遞dispatchTouchEvent、onInterceptTouchEvent

View中存在dispatchTouchEvent、onTouchEvent兩個方法。 而ViewGroup中則存在dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent三個方法。 呼叫順序依次為dispatchTouch

Androidtouchclick事件的區別

http://blog.csdn.net/hufeng882412/article/details/7310142 針對螢幕上的一個View控制元件,Android如何區分應當觸發onTouchEvent,還是onClick,亦或是onLongClick事件?在Andro

AndroidView繪製流程以及invalidate()等相關方法分析

            前言: 本文是我讀《Android核心剖析》第13章----View工作原理總結而成的,在此膜拜下作者 。同時真摯地向渴望瞭解Android 框架層的網友,推薦這本書,希望你們能夠在Android開發裡學到更多的知識 。             整個V

AndroidView事件分發機制

View事件分發機制 今天要寫一寫Android中比較重要的一個核心,View事件分發機制。那麼事件分發機制是什麼,為什麼要寫這個呢, 下面將一一講解出來。 前言 相信大家對Android基礎知識都已經有所瞭解啦,因為畢竟Android已經涼了,應該也沒有多少新

Android查缺補漏(View篇)--事件分發機制

touch事件 滑動沖突 今天 version schema ttr 步驟 isp win 事件分發機制是Android中非常重要的一個知識點,同時也是難點,相信到目前為止很多Android開發者對事件分發機制並沒有一個非常系統的認識,當然也包括博主個人在內。可能在平時的開

Android的基本元件ViewViewGroup介紹

Android的基本元件 1.1 Activity 1.1.1 Activity代表手機的一個螢幕 1.1.2 一個Android程式由多個Activity組成,即:一個Android程式由多屏內容組成 1.1.3 Activity相當於一個展板,本身

Android自定義ViewViewGroup知識點彙總

一、View的繪製流程 onMeasure()->onDraw()。 二、ViewGroup的繪製流程 onMeasure()->onLayout()->onDraw()(一般不重寫)

Android學習】關於Android解決重寫onTouch事件提示 的警告:onTouch should call View#performClick when a click is detec

一、問題的出現 當我對控制元件使用setOnTouchLister()時重寫了onTouch()方法時就出現了這個警告 二、原因 onTouchListener的onTouch方法優先順序比onTouchEvent高,會先觸發 假如onTouch方法返回fa

Android ViewGroup事件分發機制

上一篇已經完整的解析了Android View的事件分發機制,今天給大家程式碼ViewGroup事件分發的原始碼解析~~凡是自定義ViewGroup實現各種滑動效果的,不可避免的會出現很多事件的衝突,對ViewGroup事件分發機制的瞭解,也有益於大家瞭解衝突產生的原因,以及

android view 的寬度高度

請轉載的朋友標明出處~~前言:今天在onCreate 的時候獲取view 的寬度 和 高度,發現結果是0。這就奇怪了,我已經在xml 中配置了高度、寬度了,怎麼會是0呢?度娘給出了答案!但是我還是希望能結合source code來解釋一下原因,所以我跟人小結一下。首先呢,看一

android應用不響應按鍵事件(俗稱攔截按鍵)

在應用中重寫 dispatchKeyEvent函式:例項中為遮蔽KEYCODE_ENTER事件 public boolean dispatchKeyEvent(KeyEvent event) { int keyCode = event.getKeyCode();if(K

Android 原始碼解析View的touch事件分發機制

概述 本篇主要分析的是touch事件的分發機制,網上關於這個知識點的分析文章非常多。但是還是想通過結合自身的總結,來加深自己的理解。對於事件分發機制,我將使用兩篇文章對其進行分析,一篇是針對View的事件分發機制解析,一篇是針對ViewGroup的事件分發機制

解決androidviewpager內嵌html滑動事件衝突

           這幾天專案迭代,有個大的板塊是整體套用H5來做。由於H5裡還有好多小模組,需要一個滑動切換的效果,而android端只是提供一個空的fragment,佔據著viewpager的一個板塊。最開始的效果滑動起來分外尷尬。。           解決思路是這

androidview點選事件(click)view長點選事件(longclick)衝突

工作中想要實現這麼一個效果: 如圖中,當child有一個click事件,parent有一個longclick事件,當長按child的時候能夠觸發parent的longclick。 遇到的問題: 當child設定click事件時,長按child不會觸發p

解決 Android View 的 setPivotX setPivotY 不生效的問題以及設定縮放中心的方法

背景是這樣的:有一個需求要對下方的關注按鈕實現如下動畫,動畫的最後要根據滑動位置對關注按鈕進行縮放,縮放結束時整體大小為控制元件原始大小的90%,最終效果圖如下所示(模擬器是 4.2 的系統,最上面的沉浸式有點問題,忽略之): 如圖,關注按鈕向上滑

Android事件分發機制原始碼分析下----ViewGroup事件分發分析

ViewGroup事件分發機制         上篇文章從原始碼的角度對View的事件分發進行了分析,這篇文章繼續對事件分發進行介紹,從原始碼的角度分析ViewGroup的事件分發,從繼承關係看ViewGroup也屬於View的一種,但它的內部可以放置View,簡單的結論我

Android listview scrollview 滾動衝突事件的解決方法

如果一個佈局中同事引用到了listview 和scrollview 兩種滾動控制元件,那麼listview 的滾動效果將被遮蔽掉,那麼此時應加上dispatchTouchEvent()方法,然後選擇先執行滾動的控制元件加上次方法。 程式碼展示如下: package co

AndroidgetDrawablegetColor過時的替代方法

this logs con 知識 log launcher 16px ase spa 版權聲明:本文為博主原創文章,未經博主允許不得轉載。 前言 Android SDK 升級到 23 之後,getDrawable和getColor方法提示過時。 解決方案 getRe