知識要點:
1.dispatchTouchEvent(MotionEvent ev)
2.onInterceptTouchEvent(MotionEvent ev)
3.onTouchEvent(MotionEvent ev)
目標要求:
掌握事件分發(fā)機制
一. 基礎認知
- 事件分發(fā)的對象是誰着撩?點擊事件(Touch事件)
定義:當用戶觸摸屏幕時(View 或 ViewGroup派生的控件)审胸,將產生點擊事件(Touch事件)
Touch事件的相關細節(jié)(發(fā)生觸摸的位置、時間等)被封裝成MotionEvent對象事件類型(4種)
事件類型 | 具體動作 |
---|---|
MotionEvent.ACTION_DOWN | 按下View(所有事件的開始) |
MotionEvent.ACTION_UP | 抬起View(與DOWN對應) |
MotionEvent.ACTION_MOVE | 滑動View |
MotionEvent.ACTION_CANCEL | 結束事件(非人為原因) |
特別說明:從手指接觸屏幕 至 手指離開屏幕围肥,這個過程產生的一系列事件
注:一般情況下峭咒,事件列都是以DOWN事件開始鳍咱、UP事件結束荣刑,中間有無數(shù)的MOVE事件绒极,如下圖:
事件分發(fā)的本質
將點擊事件(MotionEvent)傳遞到某個具體的View & 處理的整個過程
即 事件傳遞的過程 = 分發(fā)過程骏令。-
事件在哪些對象之間進行傳遞?
Activity集峦、ViewGroup伏社、View
Android的UI界面由Activity、ViewGroup塔淤、View 及其派生類組成 事件分發(fā)的順序
即 事件傳遞的順序:Activity -> ViewGroup -> View-
事件分發(fā)過程由哪些方法協(xié)作完成?
二.分發(fā)流程
Touch事件相關方法 | Activity | ViewGroup | View |
---|---|---|---|
dispatchTouchEvent(MotionEvent ev) | 有 | 有 | 有 |
onInterceptTouchEvent(MotionEvent ev) | 沒有 | 有 | 沒有 |
onTouchEvent(MotionEvent event) | 有 | 有 | 有 |
- Activity
-
dispatchTouchEvent(MotionEvent ev) -- 事件分發(fā)
1.返回true 直接消費掉 ,沒有分發(fā);
2.返回false,不消費,但是觸摸事件不做處理;
3.只有返回super.dispatchTouchEvent(ev)事件才會向子view分發(fā),其實是調用了PhoneWindow的superDispatchTouchEvent(),進而調用了DecorView的superDispatchTouchEvent,里面又調用了super.dispatchTouchEvent(),而DecorView是一個FrameLayout
onTouchEvent(MotionEvent event) --事件處理
1.返回false和默認(super.onTouchEvent(event)),不做處理;
2.返回true,消費事件
- ViewGroup
-
dispatchTouchEvent(MotionEvent ev) -- 事件分發(fā)
1.返回true 事件被消費了, 事件未分發(fā)
2.返回false 事件未分發(fā),事件傳遞到父容器的onTouchEvent()
3.只有返回super.dispatchTouchEvent(ev),事件才會向下走,里面會調用onInterceptTouchEvent()
-
onInterceptTouchEvent(MotionEvent ev) -- 事件攔截
1.返回true,事件攔截,調用自己的onTouchEvent()
2.返回false,事件未攔截,分發(fā)給了子view
3.super.onInterceptTouchEvent(ev)與返回false效果一樣
-
onTouchEvent(MotionEvent event)
1.返回false和super.onTouchEvent(event)事件不消費,事件傳遞給父容器
2.返回true,事件會被消費
- View
-
dispatchTouchEvent(MotionEvent event)
因為它最小,沒有子view,所以這個方法其實沒有進行分發(fā)的能力
1.返回true,事件被消費,
2.返回false ,事件傳遞到父容器的onTouchEvent()
3.只有返回super.dispatchTouchEvent(ev),事件才會向下走,里面會調用自己 onTouchEvent()
-
onTouchEvent(MotionEvent event)
1.返回false和super.onTouchEvent(event)事件不消費,事件傳遞給父容器
2.返回true,事件會被消費
三.點擊事件觸發(fā)的位置及攔截
在onTouchEvent() 方法中MotionEvent.ACTION_UP中會調用performClick(),performClick()中會調用onClick()
攔截點擊事件: 在父容器中重寫 onInterceptTouchEvent(MotionEvent ev) ,返回true,會走父容器的onTouchEvent()方法
四.setOnTouchListener()和onTouchEvent()關系
源碼如下:
public boolean dispatchTouchEvent(MotionEvent event) {
...
if (onFilterTouchEventForSecurity(event)) {
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
return result;
}
第7行會調用OnTouchListener.onTouch(this, event),在第11行,才會去調用onTouchEvent(event),所以setOnTouchListener的onTouch()方法先被調用,如果onTouch()方法返回true,事件被消費了,則onTouchEvent()方法不會調用.
五.滑動事件沖突
父容器想獲取左右滑動事件,子view想獲取垂直滑動事件(如Viewpager中有ListView),都想獲取事件,怎么辦?
思路:可以進行選擇性的攔截事件,當水平滑動的距離大于垂直滑動的距離時,攔截事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//選擇性的進行攔截,當水平滑動距離大于垂直滑動距離時 ,攔截
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownX = ev.getX();
mDownY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
float moveX = ev.getX();
float moveY = ev.getY();
//水平和垂直的滑動距離(絕對值)
float dx = Math.abs(moveX - mDownX);
float dy = Math.abs(moveY- mDownY);
if (dx>dy){
return true;
}
break;
}
return false;//不攔截
}
getX()和getY():由這兩個函數(shù)獲得的x,y值是相對的坐標值速妖,相對于父容器坐標高蜂。
getRawX()和getRawY():有這兩個函數(shù)獲得的x,y值是絕對坐標,是相對于屏幕的罕容。