PS:在這個全民知識焦慌的時代,每個人都應該明白學習的重要性,應該成為「會學習的人」,而不是做「最勤奮的笨蛋」。
事件的分發流程可以說基本上已經闡述清楚,在閱讀本篇文章之前,請先閱讀下面幾篇文章:
還有一個問題是 Android 事件傳遞過程中 onTouch 和 onClick 事件在整個事件過程中是如何進行事件傳遞的,下面主要是關於 onTouch 、 onClick 與事件傳遞過程中調用的先後順序,將從如下幾個方面介紹:
- 源碼中的 onTouch () 方法
- 源碼中的 onClick () 方法
- onTouch () 與 onClick () 方法之間的關係
- 總結
源碼中的 onTouch () 方法#
當要設置觸摸事件的監聽時,使用到 View 類中的 OnTouchListener 接口,然後通過 setOnClickListener 設置對觸摸事件的監聽,然後就可以通過具體的事件類型去執行某些操作,onTouch () 方法就是接口 OnTouchListener 中定義的方法,在 View 的 dispatchTouchEvent () 方法中調用,下面是 onTouch 方法在源碼中的具體調用:
//事件分發
public boolean dispatchTouchEvent(MotionEvent event) {
...
//默認返回值
boolean result = false;
...
//注意判斷條件
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
ListenerInfo li = mListenerInfo;
//注意判斷條件
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;
}
上述代碼中,先來看一下最外面的條件 onFilterTouchEventForSecurity () 方法,只關心該方法的返回值即可,源碼如下:
/**
* 如果事件正常分發返回true,如果事件被丟棄返回false
* @see #getFilterTouchesWhenObscured
*/
public boolean onFilterTouchEventForSecurity(MotionEvent event) {
//noinspection RedundantIfStatement
if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
&& (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
// Window is obscured, drop this touch.
return false;
}
return true;
}
所以該方法正常情況下返回 true,然後關鍵的條件主要就是 ListenerInfo 是否為 null,mOnTouchListener 是否為 null,以及 onTouch () 方法的返回值,下面是 ListenerInfo 初始化的源碼部分,具體如下:
//getListenerInfo()的具體調用
public void setOnTouchListener(OnTouchListener l) {
getListenerInfo().mOnTouchListener = l;
}
//ListenerInfo的初始化
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
顯然,當通過 setOnTouchListener () 方法設置觸摸事件的監聽時就初始化了 ListenerInfo,同在在設置觸摸事件監聽的時候 mOnTouchListener != null 成立,最後 onTouch () 方法的返回值決定了 dispatchTouchEvent () 方法是否返回 true。所以,當設置了觸摸監聽事件且 onTouch () 方法返回 true 時,表示事件就此處理也就不再向子 View 傳遞了,同時,onTouchEvent () 方法也就不再執行,返回 false 則 onTouchEvent () 方法還會執行。
源碼中的 onClick () 方法#
當要設置點擊事件的事件監聽時,使用到 View 類中的 OnClickListener 接口,然後通過 setOnClickListener 設置對單擊事件的監聽,然後就可以通過具體的事件類型去執行某些操作,onClick () 方法就是 OnClickListener 接口中定義的方法,在 View 的 onTouchEvent () 方法中調用,在下文中也會進一步得到驗證,下面是 onClick () 方法在源碼中的具體調用:
//事件處理
public boolean onTouchEvent(MotionEvent event) {
...
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
case MotionEvent.ACTION_UP:
...
//如果執行了該方法,其返回值就是onTouchEvent()的返回值
performClick();
...
break;
}
return true;
}
return false;
}
上面代碼中至少找到了 onClick () 方法的調用位置,下面是 performClick () 方法:
/**
* 主要回調了 OnClickListener
*/
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
//調用了OnClickListener接口中的onClick()方法
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
顯然,只要我們在代碼中通過 setOnClickListener () 方法設置了對單擊事件的監聽,則對應 View 的 onTouchEvent () 方法返回 true,當然事件就此消費,反之返回 false,那麼 onTouch 與 onClick 之間的調用順序如何,它們之間會互相影響嗎,下面就會從案例的角度了解它們之間的關係。
onTouch () 與 onClick () 方法之間的關係#
還是之前的案例,MLinearLayout 嵌套 MRelativeLayout,MRelativeLayout 嵌套 MTextView,三個 View 都只是重寫了與它們自身相關的事件分發,然後為 MTextView 設置對觸摸事件、單擊事件的監聽,具體如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.textView).setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i("Event", "TextView-------onTouch---------------return:" + false);
return false;
}
});
findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("Event", "TextView-------onClick");
}
});
}
- 讓 onTouch () 返回 false,查看日誌如下:
結論:設置了對觸摸事件的監聽,onTouch () 方法 false 時 onTouchEvent () 方法在 onTouch () 方法之後執行,事件就此消費,接著接受 ACTION_DOWN 之後的一系列事件,途中使用鼠標,故沒有 ACTION_MOVE 事件,還有在 onTouch () 方法返回 false 的情況下 onClick () 執行了。
- 讓 onTouch () 返回 true,查看日誌如下:
結論 :當 onTouch () 返回 true 的時候,正如前面所述 onTouchEvent () 將不會再執行,故 onClick () 也就不會再執行。
總結#
onTouch () 方法的返回值決定了 onTouchEvent () 方法要不要執行,如果 onTouch () 返回 true,則 onTouchEvent () 不會再執行,返回 false , 則 onTouchEvent () 繼續執行,而 onClick () 的回調是在 onTouchEvent () 方法中調用,onTouchEvent () 不執行則 onClick () 不執行。