Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android事件分發機制分析

android事件分發機制分析

編輯:關於Android編程

觸摸事件相關方法:

ViewGroup

dispatchTouchEvent(MotionEvent) 用於分發touch事件

onInterceptTouchEvent(MotionEvent) 用於是否中斷touch事件

onTouchEvent(MotionEvent) 用於處理touch事件

View、Activity

dispatchTouchEvent(MotionEvent)onTouchEvent(MotionEvent)

具體代碼如下:

1、Utils.java 代碼如下:

public class Utils {

/**

* 獲取觸摸事件的Action名稱

* @param ev

*/

public static String getActionName(MotionEvent ev) {

String action;

switch (ev.getAction()) {

case MotionEvent.ACTION_CANCEL:

action = "CANCEL";

break;

case MotionEvent.ACTION_DOWN:

action = "DOWN";

break;

case MotionEvent.ACTION_MOVE:

action = "MOVE";

break;

case MotionEvent.ACTION_UP:

action = "UP";

break;

default:

action = "UNKNOWN_ACTION";

break;

}

if (action.length() < 5) {

for (int i = action.length(); i < 5; i++) {

action += " ";

}

}

return action;

}

}

2、ViewGroupA.java 代碼如下:

public class ViewGroupA extends LinearLayout {

public ViewGroupA(Context context, AttributeSet attrs) {

super(context, attrs);

}

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

System.out.println(Utils.getActionName(ev) + ", ViewGroupA.dispatch");

boolean result = super.dispatchTouchEvent(ev);

System.out.println(Utils.getActionName(ev) + ", ViewGroupA.dispatch = " + result);

return result;

}

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

boolean result = false;

System.out.println(Utils.getActionName(ev) + ", ViewGroupA.intercept = " + result);

return result;

}

@Override

public boolean onTouchEvent(MotionEvent event) {

boolean result = false;

System.out.println(Utils.getActionName(event) + ", ViewGroupA.touch = " + result);

return result;

}

}

布局效果如下:

 

\

 

運行代碼,在藍色的View上進行按下、移動、抬起,輸出的Log如下:

---------------------------------------------

DOWN , Activity.dispatch

DOWN , ViewGroupA.dispatch

DOWN , ViewGroupA.intercept = false

DOWN , ViewGroupB.dispatch

DOWN , ViewGroupB.intercept = false

DOWN , ViewC.dispatch

DOWN , ViewC.touch = false

DOWN , ViewC.dispatch = false

DOWN , ViewGroupB.touch = false

DOWN , ViewGroupB.dispatch = false

DOWN , ViewGroupA.touch = false

DOWN , ViewGroupA.dispatch = false

DOWN , Activity.touch = false

DOWN , Activity.dispatch = false

---------------------------------------------

MOVE , Activity.dispatch

MOVE , Activity.touch = false

MOVE , Activity.dispatch = false

---------------------------------------------

UP , Activity.dispatch

UP , Activity.touch = false

UP , Activity.dispatch = false

---------------------------------------------

從上面的Log可分析出:

觸摸事件最先是由Activity獲得,然後是ViewGroupA、ViewGroupB、ViewC

如果Down事件沒有人處理,則事件丟失,後續的事件(如Move、Up)不再傳遞,直接由Activity進行處理所有的事件分發都是調用super.dispatchTouchEvent(ev)完成的,所以如果不調用這句代碼則事件中止傳遞。但是要中止事件傳遞一般不會這麼做,一般是在onInterceptTouchEvent(MotionEvent) 方法中處理,如果該方法返回true則中止。既然dispatchTouchEvent(ev)方法可以中止事件傳遞,為什麼還要設計一個onInterceptTouchEvent(MotionEvent)方法呢? 因為子View可以請求父View不要攔截事件,如ListView是可以上下滑動的,當處於滑動狀態時候就會請求禁止父View的攔截觸摸事件方法,讓ListView可以一直獲取到touch事件進行滾動。假設這個時候父View又想響應觸摸事件怎麼辦?可以寫到dispatchTouchEvent方法中,因為事件是先傳到這個方法,然後再傳遞給ListView的。既然Activity最先獲得事件,則可在Activity的dispatchTouchEvent方法不調用super.dispatchTouchEvent(ev);代碼,則事件就不會進行分發了,可在此方法中調用onTouchEvent(ev)讓它去處理。應用:假如在Activity中要一定要響應一些觸摸事件,又怕事件傳遞後被消費了,也是相同道理,直接在Activity的dispatchTouchEvent方法中處理即可)

修改ViewGroupA的onInterceptTouchEvent讓其返回true,代表攔截事件。

運行,然後按下、移動、抬起,Log如下:

---------------------------------------------

DOWN , Activity.dispatch

DOWN , ViewGroupA.dispatch

DOWN , ViewGroupA.intercept = true

DOWN , ViewGroupA.touch = false

DOWN , ViewGroupA.dispatch = false

DOWN , Activity.touch = false

DOWN , Activity.dispatch = false

---------------------------------------------

MOVE , Activity.dispatch

MOVE , Activity.touch = false

MOVE , Activity.dispatch = false

---------------------------------------------

UP , Activity.dispatch

UP , Activity.touch = false

UP , Activity.dispatch = false

從上面的Log可分析出:

ViewGroupA在Down事件的時候中斷事件傳遞後直接把Touch事件交由自己的onTouchEvent方法去處理

ViewGroupA中斷觸摸事件後只是子View(ViewGroupB和ViewC)不再獲得touch事件,而父View(Activity)可以。雖然ViewGroupA中斷觸摸事件傳遞,由於它的onTouchEvent方法也沒有處理Down事件,所以事件也丟失了,後續事件(如Move、Up)不再傳給ViewGroupA,由Activity中行處理。所以,如果你想要獲取後續事件,在處理了Down事件後一定要記得返回true。這裡說的後續事件是指一次整體的操作,一般是由一個Down和多個Move和一個Up組成。按下然後移動然後彈起,這樣的操作稱為一次整體的操作。

修改ViewGroupA的onInterceptTouchEvent讓其返回false,代表不攔截事件

修改ViewGroupB的onInterceptTouchEvent讓其返回true,代表攔截事件,並修改onTouchEvent方法如下:

@Override

public boolean onTouchEvent(MotionEvent event) {

boolean onTouchEvent = super.onTouchEvent(event);;

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

onTouchEvent = true;

break;

case MotionEvent.ACTION_MOVE:

onTouchEvent = false;

break;

case MotionEvent.ACTION_UP:

onTouchEvent = false;

break;

default:

break;

}

System.out.println(MotionEventUtil.getMotionEventActionName(event) + ", ViewGroupB.touch = " + onTouchEvent);

return onTouchEvent;

}

運行,然後按下、移動、抬起,Log如下:

---------------------------------------------

DOWN , Activity.dispatch

DOWN , ViewGroupA.dispatch

DOWN , ViewGroupA.intercept = false

DOWN , ViewGroupB.dispatch

DOWN , ViewGroupB.intercept = true

DOWN , ViewGroupB.touch = true

DOWN , ViewGroupB.dispatch = true

DOWN , ViewGroupA.dispatch = true

DOWN , Activity.dispatch = true

---------------------------------------------

MOVE , Activity.dispatch

MOVE , ViewGroupA.dispatch

MOVE , ViewGroupA.intercept = false

MOVE , ViewGroupB.dispatch

MOVE , ViewGroupB.touch = false

MOVE , ViewGroupB.dispatch = false

MOVE , ViewGroupA.dispatch = false

MOVE , Activity.touch = false

MOVE , Activity.dispatch = false

---------------------------------------------

MOVE , Activity.dispatch

MOVE , ViewGroupA.dispatch

MOVE , ViewGroupA.intercept = false

MOVE , ViewGroupB.dispatch

MOVE , ViewGroupB.touch = false

MOVE , ViewGroupB.dispatch = false

MOVE , ViewGroupA.dispatch = false

MOVE , Activity.touch = false

MOVE , Activity.dispatch = false

---------------------------------------------

UP , Activity.dispatch

UP , ViewGroupA.dispatch

UP , ViewGroupA.intercept = false

UP , ViewGroupB.dispatch

UP , ViewGroupB.touch = false

UP , ViewGroupB.dispatch = false

UP , ViewGroupA.dispatch = false

UP , Activity.touch = false

UP , Activity.dispatch = false

從上面的Log可分析出:

ViewGroupB中斷了觸摸事件的傳遞,並把事件交給自己的onTouchEvent方法處理。它的孩子ViewC就接收不到touch事件了ViewGroupB中消費了Down事件(返回true),所以可以接收後續的Move、Up事件,dispatch方法接收到Move事件後並沒有去調用onInterceptTouchEvent方法了,因為在接收到Down事件時候已經成為了目標View,非目標View不能接收後續事件,所以後續事件(Move、Up)就不需要再調用了onInterceptTouchEvent,因為不需要再傳遞了,所以直接把事件交給自己的onTouchEvent方法進行處理。雖然ViewGroupB在處理Move事件時返回了false,但是父View(ViewGroupA)的onTouchEvent方法並沒有執行,這說明,誰消費了Down事件(返回true)誰就能接收後續事件(Move、Up),沒有消費Down事件的其它View則接收不到(Activity例外)。雖然ViewGroupB在處理Move事件時返回了false,但是還是依舊可以接收後續事件的(Move、Up),那在處理Move和UP時返回false或返回true有什麼區別嗎?答:返回true則消費此事件,Activity的onTouchEvent方法就不會接收到了。

通過這個Demo可以模擬任何的不同情況,如處理Down事件返回true,Move事件返回false,事件攔截,請求禁止攔截等等。

總結:

touch事件傳遞其實就是一連串的方法調用,由Activity的dispatchTouchEvent方法開始調用,當這個方法調用結束時,這個touch事件就結束了。

一個整體的touch事件由1個Donw和0 ~ n個Move和1個Up事件組成

消費了Down事件的View稱為目標View,目標View可接收後續事件(非目標View不接收後續事件)

Down事件如果沒有任何View消費,則後續事件不再傳遞,直接由Activity的onTouchEvent方法處理,所以,想要處理其它事件首先要消費Down事件,也就是在接收到Down事件的時候返回true。攔截: 如果在Down事件攔截,則把當前事件交自己的onTouchEvnet方法處理,如果此方法不消費Down事件,則事件丟失,後續事件由Activity的onTouchEvent方法處理如果在其它事件攔截,則不處理當前事件,且傳一個Cancel事件給目標View,後續的事件就會交給自己的onTouchEvent方法處理(此時攔截了事件的View變成了目標View,雖然它沒有消費Down事件)如果在Down事件不攔截,在事件分發返回後,如果Down事件沒被消費,則會把事件交給自己的onTouchEvent方法處理。如果在其它事件不攔截(能接收其它事件說明有目標View),在事件分發返回後,不會把事件交給自己的onTouchEvent,即使目標View在處理(Move、Up)事件時返回false。 Activity不管有無目標View,也不管是Down還是Move、Up事件,只要分發調用返回時,如果事件沒被消費,則交給自己的onTouchEvent方法處理可以調用getParent().requestDisallowInterceptTouchEvent(true)方法請求父View禁止攔截事件,這個方法會遞歸的請求所有的父View禁止攔截事件。 注:如果想要獲取到一個整體的touch事件,一定要消費Down事件,如果在Down事件的時候只是請求父View禁止攔截並不消費Down事件,雖然父View不再攔截了,但後續事件也接收不到了,哪個父View消費了Down事件哪個父View就可以接收到後續事件。 有兩個View並沒有包含關系,但是有重疊,則上面的View先拿到事件,如果消費了,則事件不會傳給另一個View。

容器類一般都是調用ViewGroup的dispatchTouchEvent方法進行事件分發,其它類一般是調用View類的dispatchTouchEvent方法進行事件分發;通常默認的onInterceptTouchEvent、onTouchEvent方法都是返回false。

應用技巧: 父類的onTouchEvent方法要想執行,要麼是等所有的子View都不消費Down事件,要麼是父View把事件攔截。如果子類消費了Down事件,而父View又不想攔截但是又想處理這個事件,則父View可以在onInterceptTouchEvent或dispatchTouchEvent方法處理touch事件如果子View請求了禁止父View攔截,且父View還想要攔截的話,可在父View的dispatchTouchEvent方法中不調用super.dispatchTouchEvent則把事件攔截了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved