Android事件傳遞機制(更加深入的瞭解事件的觸發過程)

摘要 本文爲InfoQ中文站特供稿件,首發地址爲:http://www.infoq.com/cn/articles/android-event-delivery-mechanism。如需轉載,請與InfoQ中文站聯繫。

看到好文,我都會習慣性的轉帖到自己的空間或者博客中,希望本問也可以爲大家帶來收穫

實驗環境

  • OS X 10.9
  • Eclipse(ADT)
  • Android源碼版本:API Level 19(Android 4.4)

Android事件構成

在Android中,事件主要包括點按、長按、拖拽、滑動等,點按又包括單擊和雙擊,另外還包括單指操作和多指操作。所有這些都構成了Android中的事件響應。總的來說,所有的事件都由如下三個部分作爲基礎:

  • 按下(ACTION_DOWN)
  • 移動(ACTION_MOVE)
  • 擡起(ACTION_UP)

所有的操作事件首先必須執行的是按下操作(ACTIONDOWN),之後所有的操作都是以按下操作作爲前提,當按下操作完成後,接下來可能是一段移動(ACTIONMOVE)然後擡起(ACTION_UP),或者是按下操作執行完成後沒有移動就直接擡起。這一系列的動作在Android中都可以進行控制。

我們知道,所有的事件操作都發生在觸摸屏上,而在屏幕上與我們交互的就是各種各樣的視圖組件(View),在Android中,所有的視圖都繼承於 View,另外通過各種佈局組件(ViewGroup)來對View進行佈局,ViewGroup也繼承於View。所有的UI控件例如Button、 TextView都是繼承於View,而所有的佈局控件例如RelativeLayout、容器控件例如ListView都是繼承於ViewGroup。 所以,我們的事件操作主要就是發生在View和ViewGroup之間,那麼View和ViewGroup中主要有哪些方法來對這些事件進行響應呢?記住 如下3個方法,我們通過查看View和ViewGroup的源碼可以看到:

View.java

public boolean dispatchTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event)

ViewGroup.java

public boolean dispatchTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event) 
public boolean onInterceptTouchEvent(MotionEvent event)

在View和ViewGroup中都存在dispatchTouchEvent和onTouchEvent方法,但是在ViewGroup中還有一 個onInterceptTouchEvent方法,那這些方法都是幹嘛的呢?別急,我們先看看他們的返回值。這些方法的返回值全部都是boolean型, 爲什麼是boolean型呢,看看本文的標題,“事件傳遞”,傳遞的過程就是一個接一個,那到了某一個點後是否要繼續往下傳遞呢?你發現了嗎,“是否”二 字就決定了這些方法應該用boolean來作爲返回值。沒錯,這些方法都返回true或者是false。在Android中,所有的事件都是從開始經過傳 遞到完成事件的消費,這些方法的返回值就決定了某一事件是否是繼續往下傳,還是被攔截了,或是被消費了。

接下來就是這些方法的參數,都接受了一個MotionEvent類型的參數,MotionEvent繼承於InputEvent,用於標記各種動作事件。之前提到的ACTIONDOWN、ACTIONMOVE、ACTION_UP都是MotinEvent中定義的常量。我們通過MotionEvent傳進來的事件類型來判斷接收的是哪一種類型的事件。到現在,這三個方法的返回值和參數你應該都明白了,接下來就解釋一下這三個方法分別在什麼時候處理事件。

  • dispatchTouchEvent方法用於事件的分發,Android中所有的事件都必須經過這個方法的 分發,然後決定是自身消費當前事件還是繼續往下分發給子控件處理。返回true表示不繼續分發,事件沒有被消費。返回false則繼續往下分發,如果是 ViewGroup則分發給onInterceptTouchEvent進行判斷是否攔截該事件。
  • onTouchEvent方法用於事件的處理,返回true表示消費處理當前事件,返回false則不處理,交給子控件進行繼續分發。
  • onInterceptTouchEvent是ViewGroup中才有的方法,View中沒有,它的作用是 負責事件的攔截,返回true的時候表示攔截當前事件,不繼續往下分發,交給自身的onTouchEvent進行處理。返回false則不攔截,繼續往下 傳。這是ViewGroup特有的方法,因爲ViewGroup中可能還有子View,而在Android中View中是不能再包含子View的(iOS 可以)。

到目前爲止,Android中事件的構成以及事件處理方法的作用你應該比較清楚了,接下來我們就通過一個Demo來實際體驗實驗一下。

Android事件處理

首先在Eclipse新建一個工程,並新建一個類RTButton繼承Button,用來實現我們對按鈕事件的跟蹤。

RTButton.java

public class RTButton extends Button {
	public RTButton(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			System.out.println("RTButton---dispatchTouchEvent---DOWN");
			break;
		case MotionEvent.ACTION_MOVE:
			System.out.println("RTButton---dispatchTouchEvent---MOVE");
			break;
		case MotionEvent.ACTION_UP:
			System.out.println("RTButton---dispatchTouchEvent---UP");
			break;
		default:
			break;
		}
		return super.dispatchTouchEvent(event);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			System.out.println("RTButton---onTouchEvent---DOWN");
			break;
		case MotionEvent.ACTION_MOVE:
			System.out.println("RTButton---onTouchEvent---MOVE");
			break;
		case MotionEvent.ACTION_UP:
			System.out.println("RTButton---onTouchEvent---UP");
			break;
		default:
			break;
		}
		return super.onTouchEvent(event);
	}
}

在RTButton中我重寫了dispatchTouchEvent和onTouchEvent方法,並獲取了MotionEvent各個事件狀 態,打印輸出了每一個狀態下的信息。然後在activity_main.xml中直接在根佈局下放入自定義的按鈕RTButton。

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/myLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    
    <com.ryantang.eventdispatchdemo.RTButton 
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button"/>

</LinearLayout>

接下來在Activity中爲RTButton設置onTouch和onClick的監聽器來跟蹤事件傳遞的過程,另外,Activity中也有一個dispatchTouchEvent方法和一個onTouchEvent方法,我們也重寫他們並輸出打印信息。

MainActivity.java

public class MainActivity extends Activity {
	private RTButton button;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		button = (RTButton)this.findViewById(R.id.btn);
		button.setOnTouchListener(new OnTouchListener() {
			
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				switch (event.getAction()) {
				case MotionEvent.ACTION_DOWN:
					System.out.println("RTButton---onTouch---DOWN");
					break;
				case MotionEvent.ACTION_MOVE:
					System.out.println("RTButton---onTouch---MOVE");
					break;
				case MotionEvent.ACTION_UP:
					System.out.println("RTButton---onTouch---UP");
					break;
				default:
					break;
				}
				return false;
			}
		});
		
		button.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				System.out.println("RTButton clicked!");
			}
		});
		
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
		  System.out.println("Activity---dispatchTouchEvent---DOWN");
		  break;
		case MotionEvent.ACTION_MOVE:
	          System.out.println("Activity---dispatchTouchEvent---MOVE");
		  break;
		case MotionEvent.ACTION_UP:
		  System.out.println("Activity---dispatchTouchEvent---UP");
		  break;
		default:
		  break;
		}
		return super.dispatchTouchEvent(event);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
		  System.out.println("Activity---onTouchEvent---DOWN");
		  break;
		case MotionEvent.ACTION_MOVE:
		  System.out.println("Activity---onTouchEvent---MOVE");
		  break;
		case MotionEvent.ACTION_UP:
		  System.out.println("Activity---onTouchEvent---UP");
		  break;
		default:
		  break;
		}
		return super.onTouchEvent(event);
	}
}

代碼部分已經完成了,接下來運行工程,並點擊按鈕,查看日誌輸出信息,我們可以看到如下結果:

通過日誌輸出可以看到,首先執行了Activity的dispatchTouchEvent方法進行事件分發,在MainActivity.java代碼第55行,dispatchTouchEvent方法的返回值是super.dispatchTouchEvent(event),因此調用了父類方法,我們進入Activity.java的源碼中看看具體實現。

Activity.java

/**
     * Called to process touch screen events.  You can override this to
     * intercept all touch screen events before they are dispatched to the
     * window.  Be sure to call this implementation for touch screen events
     * that should be handled normally.
     * 
     * @param ev The touch screen event.
     * 
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

從源碼中可以看到,dispatchTouchEvent方法只處理了ACTIONDOWN事件,前面提到過,所有的事件都是以按下爲起點的,所以,Android認爲當ACTIONDOWN 事件沒有執行時,後面的事件都是沒有意義的,所以這裏首先判斷ACTION_DOWN事件。如果事件成立,則調用了onUserInteraction方 法,該方法可以在Activity中被重寫,在事件被分發前會調用該方法。該方法的返回值是void型,不會對事件傳遞結果造成影響,接着會判斷 getWindow().superDispatchTouchEvent(ev)的執行結果,看看它的源碼:

Activity.java

/**
     * Used by custom windows, such as Dialog, to pass the touch screen event
     * further down the view hierarchy. Application developers should
     * not need to implement or call this.
     *
     */
    public abstract boolean superDispatchTouchEvent(MotionEvent event);

通過源碼註釋我們可以瞭解到這是個抽象方法,用於自定義的Window,例如自定義Dialog傳遞觸屏事件,並且提到開發者不需要去實現或調用該 方法,系統會完成,如果我們在MainActivity中將dispatchTouchEvent方法的返回值設爲true,那麼這裏的執行結果就爲 true,從而不會返回執行onTouchEvent(ev),如果這裏返回false,那麼最終會返回執行onTouchEvent方法,由此可知,接 下來要調用的就是onTouchEvent方法了。別急,通過日誌輸出信息可以看到,ACTION_DOWN事件從Activity被分發到了 RTButton,接着執行了onTouch和onTouchEvent方法,爲什麼先執行onTouch方法呢?我們到RTButton中的 dispatchTouchEvent看看View中的源碼是如何處理的。

View.java

/**
     * Pass the touch screen motion event down to the target view, or this
     * view if it is the target.
     *
     * @param event The motion event to be dispatched.
     * @return True if the event was handled by the view, false otherwise.
     */
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

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

            if (onTouchEvent(event)) {
                return true;
            }
        }

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }
        return false;
    }

挑選關鍵代碼進行分析,可以看代碼第16行,這裏有幾個條件,當幾個條件都滿足時該方法就返回true,當條件li.mOnTouchListener不爲空時,通過在源碼中查找,發現mOnTouchListener是在以下方法中進行設置的。

View.java

/**
     * Register a callback to be invoked when a touch event is sent to this view.
     * @param l the touch listener to attach to this view
     */
    public void setOnTouchListener(OnTouchListener l) {
        getListenerInfo().mOnTouchListener = l;
    }

這個方法就已經很熟悉了,就是我們在MainActivity.java中爲RTButton設置的 onTouchListener,條件(mViewFlags & ENABLED_MASK) == ENABLED判斷的是當前View是否是ENABLE的,默認都是ENABLE狀態的。接着就是 li.mOnTouchListener.onTouch(this, event)條件,這裏調用了onTouch方法,該方法的調用就是我們在MainActivity.java中爲RTButton設置的監聽回調,如果該方法返回true,則整個條件都滿足,dispatchTouchEvent就返回true,表示該事件就不繼續向下分發了,因爲已經被onTouch消費了。

如果onTouch返回的是false,則這個判斷條件不成立,接着執行onTouchEvent(event)方法進行判斷,如果該方法返回 true,表示事件被onTouchEvent處理了,則整個dispatchTouchEvent就返回true。到這裏,我們就可以回答之前提出的 “爲什麼先執行onTouch方法”的問題了。到目前爲止,ACTIONDOWN的事件經過了從Activity到RTButton的分發,然後經過onTouch和onTouchEvent的處理,最終,ACTIONDOWN事件交給了RTButton得onTouchEvent進行處理。

當我們的手(我這裏用的Genymotion然後用鼠標進行的操作,用手的話可能會執行一些ACTIONMOVE操作)從屏幕擡起時,會發生ACTIONUP事件。從之前輸出的日誌信心中可以看到,ACTIONUP事件同樣從Activity開始到RTButton進行分發和處理,最後,由於我們註冊了onClick事件,當onTouchEvent執行完畢後,就調用了onClick事件,那麼onClick是在哪裏被調用的呢?繼續回到View.java的源代碼中尋找。由於onTouchEvent在View.java中的源碼比較長,這裏就不貼出來了,感興趣的可以自己去研究一下,通過源碼閱讀,我們在ACTIONUP的處理分支中可以看到一個performClick()方法,從這個方法的源碼中可以看到執行了哪些操作。

View.java

/**
     * Call this view's OnClickListener, if it is defined.  Performs all normal
     * actions associated with clicking: reporting accessibility event, playing
     * a sound, etc.
     *
     * @return True there was an assigned OnClickListener that was called, false
     *         otherwise is returned.
     */
    public boolean performClick() {
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            return true;
        }

        return false;
    }

在if分支裏可以看到執行了li.mOnClickListener.onClick(this);這句代碼,這裏就執行了我們爲RTButton 實現的onClick方法,所以,到目前爲止,可以回答前一個“onClick是在哪裏被調用的呢?”的問題了,onClick是在 onTouchEvent中被執行的,並且,onClick要後於onTouch的執行。

到此,點擊按鈕的事件傳遞就結束了,我們結合源代碼窺探了其中的執行細節,如果我們修改各個事件控制方法的返回值又會發生什麼情況呢,帶着這個問題,進入下一節的討論。

Android事件攔截

從上一節分析中,我們知道了在Android中存在哪些事件類型,事件的傳遞過程以及在源碼中對應哪些處理方法。我們可以知道在Android中,事件是通過層級傳遞的,一次事件傳遞對應一個完整的層級關係,例如上節中分析的ACTIONDOWN事件從Activity傳遞到RTButton,ACTIONUP事件也同樣。結合源碼分析各個事件處理的方法,也可以明確看到事件的處理流程。

之前提過,所有事件處理方法的返回值都是boolean類型的,現在我們來修改這個返回值,首先從Activity開始,根據之前的日誌輸出結果, 首先執行的是Activity的dispatchTouchEvent方法,現在將之前的返回值 super.dispatchTouchEvent(event)修改爲true,然後重新編譯運行並點擊按鈕,看到如下的日誌輸出結果。

可以看到,事件執行到dispatchTouchEvent方法就沒有再繼續往下分發了,這也驗證了之前的說法,返回true時,不再繼續往下分 發,從之前分析過的Activity的dispatchTouchEvent源碼中也可知,當返回true時,就沒有去執行onTouchEvent方法 了。

接着,將上述修改還原,讓事件在Activity這繼續往下分發,接着就分發到了RTButton,將RTButton的dispatchTouchEvent方法的返回值修改爲true,重新編譯運行並查看輸出日誌結果。

從結果可以看到,事件在RTButton的dispatchTouchEvent方法中就沒有再繼續往下分發了。接着將上述修改還原,將 RTButton的onTouchEvent方法返回值修改爲true,讓其消費事件,根據之前的分析,onClick方法是在onTouchEvent 方法中被調用的,事件在這被消費後將不會調用onClick方法了,編譯運行,得到如下日誌輸出結果。

跟分析結果一樣,onClick方法並沒有被執行,因爲事件在RTButton的onTouchEvent方法中被消費了。下圖是整個事件傳遞的流程圖。

到目前爲止,Android中的事件攔截機制就分析完了。但這裏我們只討論了單佈局結構下單控件的情況,如果是嵌套佈局,那情況又是怎樣的呢?接下來我們就在嵌套佈局的情況下對Android的事件傳遞機制進行進一步的探究和分析。

Android嵌套佈局事件傳遞

首先,新建一個類RTLayout繼承於LinearLayout,同樣重寫dispatchTouchEvent和onTouchEvent方 法,另外,還需要重寫onInterceptTouchEvent方法,在文章開頭介紹過,這個方法只有在ViewGroup和其子類中才存在,作用是控 制是否需要攔截事件。這裏不要和dispatchTouchEvent弄混淆了,後者是控制對事件的分發,並且後者要先執行。

那麼,事件是先傳遞到View呢,還是先傳遞到ViewGroup的?通過下面的分析我們可以得出結論。首先,我們需要對工程代碼進行一些修改。

RTLayout.java

public class RTLayout extends LinearLayout {
	public RTLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
		  System.out.println("RTLayout---dispatchTouchEvent---DOWN");
		  break;
		case MotionEvent.ACTION_MOVE:
		  System.out.println("RTLayout---dispatchTouchEvent---MOVE");
		  break;
		case MotionEvent.ACTION_UP:
		  System.out.println("RTLayout---dispatchTouchEvent---UP");
		  break;
		default:
		  break;
		}
		return super.dispatchTouchEvent(event);
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			System.out.println("RTLayout---onInterceptTouchEvent---DOWN");
			break;
		case MotionEvent.ACTION_MOVE:
			System.out.println("RTLayout---onInterceptTouchEvent---MOVE");
			break;
		case MotionEvent.ACTION_UP:
			System.out.println("RTLayout---onInterceptTouchEvent---UP");
			break;
		default:
			break;
		}
		return super.onInterceptTouchEvent(event);
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			System.out.println("RTLayout---onTouchEvent---DOWN");
			break;
		case MotionEvent.ACTION_MOVE:
			System.out.println("RTLayout---onTouchEvent---MOVE");
			break;
		case MotionEvent.ACTION_UP:
			System.out.println("RTLayout---onTouchEvent---UP");
			break;
		default:
			break;
		}
		return super.onTouchEvent(event);
	}
}

同時,在佈局文件中爲RTButton添加一個父佈局,指明爲自定義的RTLayout,修改後的佈局文件如下。

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.ryantang.eventdispatchdemo.RTLayout
        android:id="@+id/myLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <com.ryantang.eventdispatchdemo.RTButton
            android:id="@+id/btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Button" />
    </com.ryantang.eventdispatchdemo.RTLayout>

</LinearLayout>

最後,我們在Activity中也爲RTLayout設置onTouch和onClick事件,在MainActivity中添加如下代碼。

MainActivity.java

rtLayout.setOnTouchListener(new OnTouchListener() {
			
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				switch (event.getAction()) {
				case MotionEvent.ACTION_DOWN:
					System.out.println("RTLayout---onTouch---DOWN");
					break;
				case MotionEvent.ACTION_MOVE:
					System.out.println("RTLayout---onTouch---MOVE");
					break;
				case MotionEvent.ACTION_UP:
					System.out.println("RTLayout---onTouch---UP");
					break;
				default:
					break;
				}
				return false;
			}
		});
		
	rtLayout.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				System.out.println("RTLayout clicked!");
			}
		});

代碼修改完畢後,編譯運行工程,同樣,點擊按鈕,查看日誌輸出結果如下:

從日誌輸出結果我們可以看到,嵌套了RTLayout以後,事件傳遞的順序變成了 Activity->RTLayout->RTButton,這也就回答了前面提出的問題,Android中事件傳遞是從ViewGroup 傳遞到View的,而不是反過來傳遞的。

從輸出結果第三行可以看到,執行了RTLayout的onInterceptTouchEvent方法,該方法的作用就是判斷是否需要攔截事件,我們到ViewGroup的源碼中看看該方法的實現。

ViewGroup.java

public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }

該方法的實現很簡單,只返回了一個false。那麼這個方法是在哪被調用的呢,通過日誌輸出分析可知它是在RTLayout的 dispatchTouchEvent執行後執行的,那我們就進到dispatchTouchEvent源碼裏面去看看。由於源碼比較長,我將其中的關鍵 部分截取出來做解釋說明。

ViewGroup.java

// Check for interception.
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true;
            }

從這部分代碼中可以看到onInterceptTouchEvent調用後返回值被賦值給intercepted,該變量控制了事件是否要向其子控 件分發,所以它起到攔截的作用,如果onInterceptTouchEvent返回false則不攔截,如果返回true則攔截當前事件。我們現在將 RTLayout中的該方法返回值修改爲true,並重新編譯運行,然後點擊按鈕,查看輸出結果如下。

可以看到,我們明明點擊的按鈕,但輸出結果顯示RTLayout點擊事件被執行了,再通過輸出結果分析,對比上次的輸出結果,發現本次的輸出結果完 全沒有RTButton的信息,沒錯,由於onInterceptTouchEvent方法我們返回了true,在這裏就將事件攔截了,所以他不會繼續分 發給RTButton了,反而交給自身的onTouchEvent方法執行了,理所當然,最後執行的就是RTLayout的點擊事件了。

總結

以上我們對Android事件傳遞機制進行了分析,期間結合系統源碼對事件傳遞過程中的處理情況進行了探究。通過單佈局情況和嵌套佈局情況下的事件傳遞和處理進行了分析,現總結如下:

  • Android中事件傳遞按照從上到下進行層級傳遞,事件處理從Activity開始到ViewGroup再到View。
  • 事件傳遞方法包括dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent,其中前兩個是View和ViewGroup都有的,最後一個是隻有ViewGroup纔有的方法。這三個方法的作用分別是負責事件分發、事件處理、事件攔截。
  • onTouch事件要先於onClick事件執行,onTouch在事件分發方法dispatchTouchEvent中調用,而 onClick在事件處理方法onTouchEvent中被調用,onTouchEvent要後於dispatchTouchEvent方法的調用。

原文地址: http://ryantang.me/blog/2014/01/02/android-event-dispatch/

發佈了4 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章