觸摸事件
在用戶觸摸屏幕時挟鸠,總是離用戶觸摸點最近的控件來響應(yīng)觸摸事件叉信,如果最近的控件沒有實現(xiàn)響應(yīng)事件,那這個事件會不斷的向父類傳遞艘希,直到有view響應(yīng)時硼身,就會將觸摸反饋的事件流傳遞給這個view的onTouchEvent()
方法,如下圖: 如果CustmoView中不響應(yīng)onTouchEvent()
覆享,那面事件會傳遞給LayoutView中佳遂,如果在LayoutView中響應(yīng)了onTouchEvent()
,那面事件就不會再傳遞給RootView了撒顿。
Android 自定義觸摸反饋事件時丑罪,通常都是如下的寫法:
public class MyView {
// ...
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//...
break;
case MotionEvent.ACTION_MOVE:
//...
break;
case MotionEvent.ACTION_UP:
//...
break;
}
return true;
}
}
復(fù)寫onTouchEvent()
然后在這里面處理觸摸反饋的事件流。
tips:
1.return true
代表本次事件流在這里消費凤壁,ACTION_DOWN
時候返回true 才是有效的吩屹。 這樣事件就不會再傳遞給父類進(jìn)行處理。
2.觸摸反饋事件流是以ACTION_DOWN
開始拧抖,以ACTION_UP
或者ACTION_CANCEL
結(jié)束的一組事件煤搜,例如:
按鈕點擊事件的觸摸反饋事件流
ACTION_DOWN
-> ACTION_MOVE
-> ACTION_MOVE
-> ACTION_UP
被中止事件的觸摸反饋事件流
ACTION_DOWN
-> ACTION_MOVE
-> ACTION_CANCEL
事件攔截
現(xiàn)在有如下這樣的一種場景:
一個Listview
, Listview
中的每一項itme中都有個Button
,Button
中的實現(xiàn) 重寫了onTouchEvent()
方法來自定義觸摸事件
場景1:
用戶點擊Button
唧席,然后松開手指擦盾。
結(jié)果:
產(chǎn)生點擊事件,事件流是這樣的:
原因:
Button
是離用戶觸摸點最近的控件淌哟,并且消費了本次的事件流迹卢。
ACTION_DOWN
-> ACTION_MOVE
-> ACTION_MOVE
-> ACTION_UP
場景2:
用戶點擊Button
,向上滑動绞绒。
結(jié)果:
不會觸發(fā)Button
的點擊事件婶希,而是Listview
開始滑動。
這次為什么不是Button
消費了本次的事件流呢蓬衡?
原因:
關(guān)鍵在onInterceptEvent()
這里喻杈。
分析:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// dispatchTouchEvent 事件分發(fā)的時候會先檢查事件是否被攔截
// Check for interception.
final boolean intercepted;
// ... 刪除了無關(guān)代碼
// Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
// ... 刪除了無關(guān)代碼
// 沒有被攔截,才會執(zhí)行之后的onTouch事件狰晚,dispatchTransformedTouchEvent 中會分發(fā)onTouchEvent事件
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
}
}
MotionEvent
事件是從根視圖開始分發(fā)的筒饰,上面的dispatchTouchEvent()
負(fù)責(zé)事件分發(fā)。 每次MotionEvent
事件都會先詢問上級視圖是否需要攔截本次事件流壁晒,一但上級視圖返回了true
瓷们,那么后續(xù)的事件流就都會直接傳遞給這個視圖的onTouchEvent()
方法,不會再傳遞給之后的視圖了。
這也就解釋了為什么點擊Button
谬晕,向上滑動不是觸發(fā)點擊事件而是觸發(fā)了Listview
的滑動事件碘裕。這是因為Listview
在onInterceptEvent()
中判斷出本次是滑動事件,從而攔截了本次事件流攒钳,來讓自己處理本次事件流帮孔。
tips:
1. onTouchEvent() 函數(shù)中只有ACTION_DOWN時返回true才是有效的,若ACTION_DOWN沒有返回true不撑,那么后續(xù)的事件流也就不會再進(jìn)來了文兢,和這個view也就無緣了。
1. onInterceptEvent() 函數(shù)中可以在最開始ACTION_DOWN時返回false焕檬,然后再之后的事件流中來判斷是否需要開始攔截本次事件流姆坚,也就是說可以在之后事件流的過程中來判斷是否達(dá)到觸發(fā)攔截條件,從而來開始攔截
阻止上級事件攔截
現(xiàn)在有如下這樣的一種場景:
在一個類似Listview
的支持滾動的自定義View
中, View
中有個Button
实愚,Button
中的實現(xiàn) 重寫了onTouchEvent()
方法來自定義觸摸事件兼呵,長按后支持Button
上下移動。
場景3:
用戶點擊Button
爆侣,長按后向上滑動萍程。
結(jié)果:
不會觸發(fā)View
滑動,而是Button
在移動
這次為什么View
中的onInterceptEvent()
沒有攔截到移動的事件流呢兔仰?
原因:
關(guān)鍵在requestDisallowInterceptTouchEvent()
這里
分析:
requestDisallowInterceptTouchEvent()
是告訴上級視圖,不要攔截本次的事件流蕉鸳。
這個設(shè)置是臨時的乎赴,也就是只對本次事件流有效。 下次事件流發(fā)生時候潮尝,如果需要還必須要重新調(diào)用一次榕吼。
End!