search
尋找貓咪~QQ 地點 桃園市桃園區 Taoyuan , Taoyuan

android事件傳遞機制詳解

本篇內容將結合Android源碼來分析Android的事件傳遞機制。眾所周知,點按、滑動、觸摸構成了Android等智能設備的基本操作,幾乎所有的應用都通過對觸摸屏的操作來進行應用程序的使用。那麼,在Android中,觸摸事件是如何響應及傳遞的呢,通過本篇內容你將有一個初步的了解。

實驗環境

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

Android事件構成

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

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

所有的操作事件首先必須執行的是按下操作(ACTION_DOWN),之後所有的操作都是以按下操作作為前提,當按下操作完成後,接下來可能是一段移動(ACTION_MOVE)然後抬起(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

1 2 public boolean dispatchTouchEvent(MotionEvent event) public boolean onTouchEvent(MotionEvent event)
1 2 3 public boolean dispatchTouchEvent(MotionEvent event) public boolean onTouchEvent(MotionEvent event) public boolean onInterceptTouchEvent(MotionEvent ev)

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

接下來就是這些方法的參數,都接受了一個MotionEvent類型的參數,MotionEvent繼承於InputEvent,用於標記各種動作事件。之前提到的ACTION_DOWN、ACTION_MOVE、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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 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

"match_parent""match_parent"

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

MainActivity.java

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /** * 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方法只處理了ACTION_DOWN事件,前面提到過,所有的事件都是以按下為起點的,所以,Android認為當ACTION_DOWN事件沒有執行時,後面的事件都是沒有意義的,所以這裡首先判斷ACTION_DOWN事件。如果事件成立,則調用了onUserInteraction方法,該方法可以在Activity中被重寫,在事件被分發前會調用該方法。該方法的返回值是void型,不會對事件傳遞結果造成影響,接著會判斷getWindow.superDispatchTouchEvent(ev)的執行結果,看看它的源碼:

Activity.java

1 2 3 4 5 6 7 /** * 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



熱門推薦

本文由 yidianzixun 提供 原文連結

寵物協尋 相信 終究能找到回家的路
寫了7763篇文章,獲得2次喜歡
留言回覆
回覆
精彩推薦