編輯:關於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 背景今天突然想起之前在上家公司(做TV與BOX盒子)時有好幾個人問過我關於Android的Context到底是啥的問題,所以就馬上要誕生這篇文章。我們平時
先看看效果: 首先,導入包:compile files(libs/nineoldandroids-2.4.0.jar)r然後在main中創建一個widget包。 c創建V
android中timer的應用.本示例中采用的是用timer來控制ProgressBar的響應. private Timer timer = n