public boolean dispatchTouchEvent(MotionEvent ev) { //第一步 //通常事件列開始都是DOWN,因此這裏基本是true if (ev.getAction() == MotionEvent.ACTION_DOWN) { //第二步 onUserInteraction(); } //第三步 if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); }
public void onUserInteraction() { }
@Override public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); //mDecor是DecorView的實例 //DecorView是視圖的頂層view,繼承自FrameLayout,是全部界面的父類 }
public boolean superDispatchTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event); //DecorView繼承自FrameLayout //那麼它的父類就是ViewGroup 而super.dispatchTouchEvent(event)方法,其實就應該是ViewGroup的dispatchTouchEvent() }
// 發生ACTION_DOWN事件或者已經發生過ACTION_DOWN,而且將mFirstTouchTarget賦值,才進入此區域,主要功能是攔截器 final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) { //disallowIntercept:是否禁用事件攔截的功能(默認是false),即不由用 //能夠在子View經過調用requestDisallowInterceptTouchEvent方法對這個值進行修改,不讓該View攔截事件 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; //默認狀況下會進入該方法 if (!disallowIntercept) { //調用攔截方法 intercepted = onInterceptTouchEvent(ev); ev.setAction(action); } else { intercepted = false; } } else { // 當沒有觸摸targets,且不是down事件時,開始持續攔截觸摸。 intercepted = true; }
/* 從最底層的父視圖開始遍歷, ** 找尋newTouchTarget,即上面的mFirstTouchTarget ** 若是已經存在找尋newTouchTarget,說明正在接收觸摸事件,則跳出循環。 */ for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; final View child = (preorderedList == null) ? children[childIndex] : preorderedList.get(childIndex); // 若是當前視圖沒法獲取用戶焦點,則跳過本次循環 if (childWithAccessibilityFocus != null) { if (childWithAccessibilityFocus != child) { continue; } childWithAccessibilityFocus = null; i = childrenCount - 1; } //若是view不可見,或者觸摸的座標點不在view的範圍內,則跳過本次循環 if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; } newTouchTarget = getTouchTarget(child); // 已經開始接收觸摸事件,並退出整個循環。 if (newTouchTarget != null) { newTouchTarget.pointerIdBits |= idBitsToAssign; break; } //重置取消或擡起標誌位 //若是觸摸位置在child的區域內,則把事件分發給子View或ViewGroup if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // 獲取TouchDown的時間點 mLastTouchDownTime = ev.getDownTime(); // 獲取TouchDown的Index if (preorderedList != null) { for (int j = 0; j < childrenCount; j++) { if (children[childIndex] == mChildren[j]) { mLastTouchDownIndex = j; break; } } } else { mLastTouchDownIndex = childIndex; } //獲取TouchDown的x,y座標 mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); //添加TouchTarget,則mFirstTouchTarget != null。 newTouchTarget = addTouchTarget(child, idBitsToAssign); //表示以及分發給NewTouchTarget alreadyDispatchedToNewTouchTarget = true; break; }
dispatchTransformedTouchEvent()
方法實際就是調用子元素的dispatchTouchEvent()
方法。dispatchTransformedTouchEvent()
方法的重要邏輯以下:if (child == null) { handled = super.dispatchTouchEvent(event); } else { handled = child.dispatchTouchEvent(event); }
//添加TouchTarget,則mFirstTouchTarget != null。 newTouchTarget = addTouchTarget(child, idBitsToAssign); //表示以及分發給NewTouchTarget alreadyDispatchedToNewTouchTarget = true;
addTouchTarget(child, idBitsToAssign);
內部完成mFirstTouchTarget被賦值。若是mFirstTouchTarget爲空,將會讓ViewGroup默認攔截全部操做。若是遍歷全部子View或ViewGroup,都沒有消費事件。ViewGroup會本身處理事件。public boolean dispatchTouchEvent(MotionEvent event) { if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) { return true; } return onTouchEvent(event); }
第一個條件:mOnTouchListener != null; 第二個條件:(mViewFlags & ENABLED_MASK) == ENABLED; 第三個條件:mOnTouchListener.onTouch(this, event);
//mOnTouchListener是在View類下setOnTouchListener方法裏賦值的 public void setOnTouchListener(OnTouchListener l) { //即只要咱們給控件註冊了Touch事件,mOnTouchListener就必定被賦值(不爲空) mOnTouchListener = l; }
//手動調用設置 button.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return false; } });
public boolean onTouchEvent(MotionEvent event) { final int viewFlags = mViewFlags; if ((viewFlags & ENABLED_MASK) == DISABLED) { // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. return (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)); } if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; } } //若是該控件是能夠點擊的就會進入到下兩行的switch判斷中去; if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { //若是當前的事件是擡起手指,則會進入到MotionEvent.ACTION_UP這個case當中。 switch (event.getAction()) { case MotionEvent.ACTION_UP: boolean prepressed = (mPrivateFlags & PREPRESSED) != 0; // 在通過種種判斷以後,會執行到關注點1的performClick()方法。 //請往下看關注點1 if ((mPrivateFlags & PRESSED) != 0 || prepressed) { // take focus if we don't have it already and we should in // touch mode. boolean focusTaken = false; if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { focusTaken = requestFocus(); } if (!mHasPerformedLongPress) { // This is a tap, so remove the longpress check removeLongPressCallback(); // Only perform take click actions if we were in the pressed state if (!focusTaken) { // Use a Runnable and post this rather than calling // performClick directly. This lets other visual state // of the view update before click actions start. if (mPerformClick == null) { mPerformClick = new PerformClick(); } if (!post(mPerformClick)) { //關注點1 //請往下看performClick()的源碼分析 performClick(); } } } if (mUnsetPressedState == null) { mUnsetPressedState = new UnsetPressedState(); } if (prepressed) { mPrivateFlags |= PRESSED; refreshDrawableState(); postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration()); } else if (!post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } removeTapCallback(); } break; case MotionEvent.ACTION_DOWN: if (mPendingCheckForTap == null) { mPendingCheckForTap = new CheckForTap(); } mPrivateFlags |= PREPRESSED; mHasPerformedLongPress = false; postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); break; case MotionEvent.ACTION_CANCEL: mPrivateFlags &= ~PRESSED; refreshDrawableState(); removeTapCallback(); break; case MotionEvent.ACTION_MOVE: final int x = (int) event.getX(); final int y = (int) event.getY(); // Be lenient about moving outside of buttons int slop = mTouchSlop; if ((x < 0 - slop) || (x >= getWidth() + slop) || (y < 0 - slop) || (y >= getHeight() + slop)) { // Outside button removeTapCallback(); if ((mPrivateFlags & PRESSED) != 0) { // Remove any future long press/tap checks removeLongPressCallback(); // Need to switch from pressed to not pressed mPrivateFlags &= ~PRESSED; refreshDrawableState(); } } break; } //若是該控件是能夠點擊的,就必定會返回true return true; } //若是該控件是不能夠點擊的,就必定會返回false return false; }
public boolean performClick() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); if (mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); mOnClickListener.onClick(this); return true; } return false; }
public void setOnClickListener(OnClickListener l) { if (!isClickable()) { setClickable(true); } mOnClickListener = l; }
TextView textView = findViewById(R.id.tv_13);
//設置OnTouchListener() textView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Log.d("小楊逗比","執行了onTouch(), 動做是:" + event.getAction()); return true; } }); //設置OnClickListener textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d("小楊逗比","執行了onClick()"); } });
2019-04-04 13:37:58.301 13616-13616/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動做是:0 2019-04-04 13:37:58.315 13616-13616/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動做是:2 2019-04-04 13:37:58.405 13616-13616/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動做是:2 2019-04-04 13:37:58.408 13616-13616/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動做是:1
2019-04-04 13:41:26.961 14006-14006/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動做是:0 2019-04-04 13:41:26.978 14006-14006/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動做是:2 2019-04-04 13:41:27.072 14006-14006/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動做是:2 2019-04-04 13:41:27.074 14006-14006/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動做是:1 2019-04-04 13:41:27.076 14006-14006/org.yczbj.ycrefreshview D/小楊逗比: 執行了onClick()
//&&爲短路與,即若是前面條件爲false,將再也不往下執行 //因此,onTouch可以獲得執行須要兩個前提條件: //1. mOnTouchListener的值不能爲空 //2. 當前點擊的控件必須是enable的。 mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)