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()
}
複製代碼
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
LogUtils.e("yc----------事件攔截----------");
return super.onInterceptTouchEvent(e);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
LogUtils.e("yc----------事件分發----------");
return super.dispatchTouchEvent(ev);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent e) {
LogUtils.e("yc----------事件觸摸----------");
return super.onTouchEvent(e);
}
複製代碼
yc----------事件分發----------
yc----------事件攔截----------
yc----------事件觸摸----------
複製代碼
// 點擊事件產生後,會直接調用dispatchTouchEvent分發方法
public boolean dispatchTouchEvent(MotionEvent ev) {
//表明是否消耗事件
boolean consume = false;
if (onInterceptTouchEvent(ev)) {
//若是onInterceptTouchEvent()返回true則表明當前View攔截了點擊事件
//則該點擊事件則會交給當前View進行處理
//即調用onTouchEvent ()方法去處理點擊事件
consume = onTouchEvent (ev) ;
} else {
//若是onInterceptTouchEvent()返回false則表明當前View不攔截點擊事件
//則該點擊事件則會繼續傳遞給它的子元素
//子元素的dispatchTouchEvent()就會被調用,重複上述過程
//直到點擊事件被最終處理爲止
consume = child.dispatchTouchEvent (ev) ;
}
return consume;
}
複製代碼
// 發生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)
複製代碼