1. 這裏咱們先從案例角度說明dispatchTouchEvent,onTouch,onTouchEvent,onClick邏輯順序過程:android
(1)首先咱們重寫一個MyButton 繼承自 Button,代碼以下:app
1 package com.himi.eventdemo; 2 3 import android.content.Context; 4 import android.util.AttributeSet; 5 import android.util.Log; 6 import android.view.MotionEvent; 7 import android.widget.Button; 8 9 public class MyButton extends Button { 10 11 private static String TAG = "MyButton"; 12 public MyButton(Context context) { 13 super(context); 14 } 15 16 public MyButton(Context context, AttributeSet attrs) { 17 super(context, attrs); 18 } 19 20 public MyButton(Context context, AttributeSet attrs, int defStyleAttr) { 21 super(context, attrs, defStyleAttr); 22 } 23 24 25 @Override 26 public boolean dispatchTouchEvent(MotionEvent event) { 27 28 switch (event.getAction()) { 29 case MotionEvent.ACTION_DOWN: 30 Log.e(TAG,"dispatchTouchEvent====MyButton=====ACTION_DOWN"); 31 break; 32 case MotionEvent.ACTION_MOVE: 33 Log.e(TAG,"dispatchTouchEvent====MyButton=====ACTION_MOVE"); 34 break; 35 case MotionEvent.ACTION_UP: 36 Log.e(TAG,"dispatchTouchEvent====MyButton=====ACTION_UP"); 37 break; 38 } 39 40 return super.dispatchTouchEvent(event); 41 } 42 43 @Override 44 public boolean onTouchEvent(MotionEvent event) { 45 46 switch (event.getAction()) { 47 case MotionEvent.ACTION_DOWN: 48 Log.e(TAG,"onTouchEvent====MyButton=====ACTION_DOWN"); 49 break; 50 case MotionEvent.ACTION_MOVE: 51 Log.e(TAG,"onTouchEvent====MyButton=====ACTION_MOVE"); 52 break; 53 case MotionEvent.ACTION_UP: 54 Log.e(TAG,"onTouchEvent====MyButton=====ACTION_UP"); 55 break; 56 } 57 58 return super.onTouchEvent(event); 59 } 60 }
(2)來到主佈局文件activity_main.xml,以下:ide
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:gravity="center" 6 tools:context="com.himi.eventdemo.MainActivity" > 7 8 <com.himi.eventdemo.MyButton 9 android:id="@+id/myButton" 10 android:layout_width="wrap_content" 11 android:layout_height="wrap_content" 12 android:text="測試" 13 /> 14 15 </RelativeLayout>
(3)測試MainActivity,以下:源碼分析
1 package com.himi.eventdemo; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.util.Log; 6 import android.view.MotionEvent; 7 import android.view.View; 8 import android.widget.Button; 9 10 public class MainActivity extends Activity { 11 12 private static String TAG ="MainActivity"; 13 14 15 private Button myButton; 16 17 18 @Override 19 protected void onCreate(Bundle savedInstanceState) { 20 super.onCreate(savedInstanceState); 21 setContentView(R.layout.activity_main); 22 23 myButton = (Button) findViewById(R.id.myButton); 24 25 myButton.setOnTouchListener(new View.OnTouchListener() { 26 @Override 27 public boolean onTouch(View v, MotionEvent event) { 28 switch (event.getAction()) { 29 case MotionEvent.ACTION_DOWN: 30 Log.e(TAG,"onTouch====MyButton=====ACTION_DOWN"); 31 break; 32 case MotionEvent.ACTION_MOVE: 33 Log.e(TAG,"onTouch====MyButton=====ACTION_MOVE"); 34 break; 35 case MotionEvent.ACTION_UP: 36 Log.e(TAG,"onTouch====MyButton=====ACTION_UP"); 37 break; 38 } 39 return false; 40 } 41 }); 42 43 myButton.setOnClickListener(new View.OnClickListener() { 44 @Override 45 public void onClick(View v) { 46 Log.e(TAG,"onClick====MyButton=====onClick"); 47 } 48 }); 49 50 51 } 52 53 54 }
(4)部署程序到手機上,以下:佈局
點擊測試按鈕,打印結果以下:post
從上面打印的結果分析:測試
點擊Button按鈕事件分發過程以下:this
dispatchTouchEvent --> onTouch --> onTouchEvent --> onClickspa
相信細心的你確定發現了,都是在ACTION_UP事件以後才觸發onClick點擊事件。3d
2. 下面咱們從源碼的角度分析dispatchTouchEvent,onTouch,onTouchEvent,onClick邏輯順序過程:
(1)事件分發都是從dispatchTouchEvent方法開始的,那麼咱們這裏是重寫了dispatchTouchEvent方法,而且最後也調用了父類的super.dispatchTouchEvent(event)方法。那麼咱們看看父類中的方法到底作了什麼??點擊進入父類的dispatchTouchEvent方法,發現此方法在View類中找到,其實也不奇怪,全部控件的父類都是View。這裏我貼出最新源碼以下:
1 public boolean dispatchTouchEvent(MotionEvent event) { 2 boolean result = false; 3 4 if (mInputEventConsistencyVerifier != null) { 5 mInputEventConsistencyVerifier.onTouchEvent(event, 0); 6 } 7 8 final int actionMasked = event.getActionMasked(); 9 if (actionMasked == MotionEvent.ACTION_DOWN) { 10 // Defensive cleanup for new gesture 11 stopNestedScroll(); 12 } 13 14 if (onFilterTouchEventForSecurity(event)) { 15 //noinspection SimplifiableIfStatement 16 ListenerInfo li = mListenerInfo; 17 if (li != null && li.mOnTouchListener != null 18 && (mViewFlags & ENABLED_MASK) == ENABLED 19 && li.mOnTouchListener.onTouch(this, event)) { 20 result = true; 21 } 22 23 if (!result && onTouchEvent(event)) { 24 result = true; 25 } 26 } 27 28 if (!result && mInputEventConsistencyVerifier != null) { 29 mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); 30 } 31 32 // Clean up after nested scrolls if this is the end of a gesture; 33 // also cancel it if we tried an ACTION_DOWN but we didn't want the rest 34 // of the gesture. 35 if (actionMasked == MotionEvent.ACTION_UP || 36 actionMasked == MotionEvent.ACTION_CANCEL || 37 (actionMasked == MotionEvent.ACTION_DOWN && !result)) { 38 stopNestedScroll(); 39 } 40 41 return result; 42 }
忽略其餘無關代碼,咱們直接看17--25行。
第17行的 if 判斷關鍵在於li.mOnTouchListener.onTouch(this, event) 的返回值,
這個接口回調就是咱們外面寫的myButton.setOnTouchListener事件(Button 的onTouch事件),
在MainActivity代碼裏,咱們setOnTouchListener返回的值是false,因此在源碼中咱們能夠看到 17行的條件不成立,那麼條件不成立,result=false;
所以,源碼的第23行 if 判斷第一個條件成立,繼續執行第二個條件,也就是onTouchEvent。咱們跳到這個方法裏看看裏面幹啥了?看以下代碼:
1 public boolean onTouchEvent(MotionEvent event) { 2 3 if (((viewFlags & CLICKABLE) == CLICKABLE || 4 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { 5 switch (event.getAction()) { 6 case MotionEvent.ACTION_UP: 7 boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0; 8 if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) { 9 // take focus if we don't have it already and we should in 10 // touch mode. 11 boolean focusTaken = false; 12 if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { 13 focusTaken = requestFocus(); 14 } 15 16 if (prepressed) { 17 // The button is being released before we actually 18 // showed it as pressed. Make it show the pressed 19 // state now (before scheduling the click) to ensure 20 // the user sees it. 21 setPressed(true, x, y); 22 } 23 24 if (!mHasPerformedLongPress) { 25 // This is a tap, so remove the longpress check 26 removeLongPressCallback(); 27 28 // Only perform take click actions if we were in the pressed state 29 if (!focusTaken) { 30 // Use a Runnable and post this rather than calling 31 // performClick directly. This lets other visual state 32 // of the view update before click actions start. 33 if (mPerformClick == null) { 34 mPerformClick = new PerformClick(); 35 } 36 if (!post(mPerformClick)) { 37 performClick(); 38 } 39 } 40 } 41 42 if (mUnsetPressedState == null) { 43 mUnsetPressedState = new UnsetPressedState(); 44 } 45 46 if (prepressed) { 47 postDelayed(mUnsetPressedState, 48 ViewConfiguration.getPressedStateDuration()); 49 } else if (!post(mUnsetPressedState)) { 50 // If the post failed, unpress right now 51 mUnsetPressedState.run(); 52 } 53 54 removeTapCallback(); 55 } 56 break; 57 return true; 58 } 59 60 return false; 61 }
咱們看看這裏邊都作了些什麼,忽略其餘,咱們直接看37行的 performClick(); 方法,跳進去繼續看:
(注意:這裏的performClick方法是在ACTION_UP手勢裏邊執行的哦!!!)
1 public boolean performClick() { 2 final boolean result; 3 final ListenerInfo li = mListenerInfo; 4 if (li != null && li.mOnClickListener != null) { 5 playSoundEffect(SoundEffectConstants.CLICK); 6 li.mOnClickListener.onClick(this); 7 result = true; 8 } else { 9 result = false; 10 } 11 12 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); 13 return result; 14 }
看見沒??
第6行 li.mOnClickListener.onClick(this);
這個接口回調就是咱們Button的 onClick事件。到此爲止,咱們從源碼分析了Button事件分發過程。
結論:
dispatchTouchEvent---->onTouch---->onTouchEvent----->onClick。而且若是仔細的你會發現,在onTouchEvent方法內部判斷執行onClick方法,可是,在全部ACTION_UP事件以後才觸發onClick點擊事件。
3. 如今咱們來看看其餘狀況:當onTouch返回爲true,打印結果以下:
1 package com.himi.eventdemo; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.util.Log; 6 import android.view.MotionEvent; 7 import android.view.View; 8 import android.widget.Button; 9 10 public class MainActivity extends Activity { 11 12 private static String TAG ="MainActivity"; 13 14 15 private Button myButton; 16 17 18 @Override 19 protected void onCreate(Bundle savedInstanceState) { 20 super.onCreate(savedInstanceState); 21 setContentView(R.layout.activity_main); 22 23 myButton = (Button) findViewById(R.id.myButton); 24 25 myButton.setOnTouchListener(new View.OnTouchListener() { 26 @Override 27 public boolean onTouch(View v, MotionEvent event) { 28 switch (event.getAction()) { 29 case MotionEvent.ACTION_DOWN: 30 Log.e(TAG,"onTouch====MyButton=====ACTION_DOWN"); 31 break; 32 case MotionEvent.ACTION_MOVE: 33 Log.e(TAG,"onTouch====MyButton=====ACTION_MOVE"); 34 break; 35 case MotionEvent.ACTION_UP: 36 Log.e(TAG,"onTouch====MyButton=====ACTION_UP"); 37 break; 38 } 39 return true; 40 } 41 }); 42 43 myButton.setOnClickListener(new View.OnClickListener() { 44 @Override 45 public void onClick(View v) { 46 Log.e(TAG,"onClick====MyButton=====onClick"); 47 } 48 }); 49 50 51 } 52 53 54 }
打印結果以下:
結論:
dispatchTouchEvent---->onTouch
驚奇的發現,居然沒有執行onClick事件是吧????若是你仔細閱讀上面的文章,估計你知道爲何了吧?仍是跟你們一塊兒分析一下吧:源碼以下:
1 public boolean dispatchTouchEvent(MotionEvent event) { 2 boolean result = false; 3 4 if (mInputEventConsistencyVerifier != null) { 5 mInputEventConsistencyVerifier.onTouchEvent(event, 0); 6 } 7 8 final int actionMasked = event.getActionMasked(); 9 if (actionMasked == MotionEvent.ACTION_DOWN) { 10 // Defensive cleanup for new gesture 11 stopNestedScroll(); 12 } 13 14 if (onFilterTouchEventForSecurity(event)) { 15 //noinspection SimplifiableIfStatement 16 ListenerInfo li = mListenerInfo; 17 if (li != null && li.mOnTouchListener != null 18 && (mViewFlags & ENABLED_MASK) == ENABLED 19 && li.mOnTouchListener.onTouch(this, event)) { 20 result = true; 21 } 22 23 if (!result && onTouchEvent(event)) { 24 result = true; 25 } 26 } 27 28 if (!result && mInputEventConsistencyVerifier != null) { 29 mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); 30 } 31 32 // Clean up after nested scrolls if this is the end of a gesture; 33 // also cancel it if we tried an ACTION_DOWN but we didn't want the rest 34 // of the gesture. 35 if (actionMasked == MotionEvent.ACTION_UP || 36 actionMasked == MotionEvent.ACTION_CANCEL || 37 (actionMasked == MotionEvent.ACTION_DOWN && !result)) { 38 stopNestedScroll(); 39 } 40 41 return result; 42 }
從第 17 行能夠看出,條件成立,result=true;
那麼第 23 行 if 條件根本不會執行第二個判斷,那麼就不會執行onTouchEvent方法,也就不會調用 onClick的接口,所以Button 不會執行setOnClickListener中的onClick事件。
4. 總結: