事件分發原始碼分析
View的TouchEvent事件分發原始碼
View 與 Touch 相關的有兩個非常重要的方法
- dispatchTouchEvent 事件分發
//預設是false
boolean result = false;
// ListenerInfo li = mListenerInfo;
ListenerInfo li = mListenerInfo;
//如果是enabled而且觸控事件返回為true,則返回true
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//如果result為false則執行onTouchEvent方法,若執行的onTouchEvent為true,則result為true
if (!result && onTouchEvent(event)) {
result = true;
}
點選事件——>在View的onTouchEvent -> case MotionEvent.ACTION_UP: 裡面呼叫了 performClick()——>li.mOnClickListener.onClick(this); 點選事件
- onTouchEvent方法(一般都會被我們複寫)
測試
首先自定義view
public class TouchView extends View {
public TouchView(Context context) {
super(context);
}
public TouchView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public TouchView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("TAG", "onTouchEvent--->" + event.getAction());
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e("TAG", "dispatchTouchEvent--->" + event.getAction());
return super.dispatchTouchEvent(event);
}
}
使用自定義view
View view = findViewById(R.id.touch_view);
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("TAG","onTouch-->"+event.getAction());
return false;
}
});
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("TAG","onClick");
}
});
分析(0代表down,1代表up,2代表move)
第一種場景:OnTouchListener onTouchEvent dispatchTouchEvent OnClickListener 四個都有的情況下 前提是OnTouchListener 返回false
結果:
dispatchTouchEvent—>0
onTouch–>0
onTouchEvent—>0
dispatchTouchEvent—>2
onTouch–>2
onTouchEvent—>2
dispatchTouchEvent—>1
onTouch–>1
onTouchEvent—>1
onClick
第二種場景:OnTouchListener onTouchEvent dispatchTouchEvent OnClickListener 四個都有的情況下 前提是OnTouchListener 返回true,其他不動
View view = findViewById(R.id.touch_view);
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("TAG","onTouch-->"+event.getAction());
return true;
}
});
此時li.mOnTouchListener.onTouch(this, event)) 方法返回true則不會執行onTouchEvent方法
結果
dispatchTouchEvent—>0
onTouch–>0
dispatchTouchEvent—>2
onTouch–>2
dispatchTouchEvent—>1
onTouch–>1
第三種場景:onTouchEvent dispatchTouchEvent OnClickListener 三個的情況下 設定OnTouchEvent為true
OnTouchEvent預設點選了返回的是true
結果
dispatchTouchEvent—>0
onTouchEvent—>0
dispatchTouchEvent—>2
onTouchEvent—>2
dispatchTouchEvent—>2
dispatchTouchEvent—>1
onTouchEvent—>1
return super.onTouchEvent(event)和return true是有區別的,當設定為true時不會有onClick方法,而預設值會
原因:當設定為true時不會進入view中的onTouchEvent方法
第四種場景:OnTouchListener onTouchEvent dispatchTouchEvent OnClickListener 四個的情況下 設定dispatchTouchEvent 為true,其他的返回原樣
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e("TAG", "dispatchTouchEvent--->" + event.getAction());
return true;
}
結果
dispatchTouchEvent—>0
dispatchTouchEvent—>2
dispatchTouchEvent—>1
view的dispatch的方法不會被執行
ViewGroup 的事件分發 原始碼分析
首先準備工作
將原本activity_main佈局中的LinearLayout換成自定義View的TouchViewGroup
public class TouchViewGroup extends LinearLayout {
public TouchViewGroup(Context context) {
super(context);
}
public TouchViewGroup(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public TouchViewGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//事件分發
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e("TAG", "ViewGroup dispatchTouchEvent--->" + event.getAction());//這是自己的處理事件
return super.dispatchTouchEvent(event);
}
//事件攔截
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("TAG", "ViewGroup onInterceptTouchEvent--->" + ev.getAction());//這是自己的處理事件
return super.onInterceptTouchEvent(ev);
}
//事件觸控
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("TAG", "ViewGroup onTouchEvent--->" + event.getAction());//這是自己的處理事件
return super.onTouchEvent(event);
}
}
並修改其中的view列印事件
View view = findViewById(R.id.touch_view);
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("TAG", "View onTouch-->" + event.getAction());
return false;
}
});
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("TAG", "onClick");
}
});
public class TouchView extends View {
public TouchView(Context context) {
super(context);
}
public TouchView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public TouchView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("TAG", "View onTouchEvent--->" + event.getAction());//這是自己的處理事件
return super.onTouchEvent(event);//預設返回true
//而點選事件是系統的UP中的事件,所以你返回true的時候並沒有進入系統中的
//return true;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e("TAG", "View dispatchTouchEvent--->" + event.getAction());
return super.dispatchTouchEvent(event);
}
}
dispatchTouchEvent() 原始碼看看
boolean handled = false;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// 清除TouchTargets 只要知道 mFirstTouchTarget = null
cancelAndClearTouchTargets(ev);
resetTouchState();
}
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {//disallowIntercept==false
intercepted = onInterceptTouchEvent(ev);//預設返回false
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
TouchTarget newTouchTarget = null;
if (!canceled && !intercepted) {
if (newTouchTarget == null && childrenCount != 0) {//ACTION_DOWN
for (int i = childrenCount - 1; i >= 0; i--) {// 反序的for迴圈 獲取子View child
newTouchTarget = getTouchTarget(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// 如果子 View 返回true 就會進來 主要給 mFirstTouchTarget = target; 賦值
addTouchTarget(child, idBitsToAssign);
}
}
}
}
/**
* Clears all touch targets. * mFirstTouchTarget = null; 這句話核心清除 mFirstTouchTarget
*/
private void clearTouchTargets() {
TouchTarget target = mFirstTouchTarget;
if (target != null) {
do {
TouchTarget next = target.next;
target.recycle();
target = next;
} while (target != null);
mFirstTouchTarget = null;
}
}
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any transformations
// or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
// child == null 會呼叫 自己的 super View.dispatchTouchEvent(event)
handled = super.dispatchTouchEvent(event);
} else {
// child == null 會呼叫 自己的 super View.dispatchTouchEvent(event)
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
}
onInterceptTouchEvent() 原始碼看看
onTouchEvent() 原始碼看看
場景分析
第一種常見,View和ViewGroup的TouchEvent全部設定為預設
結果:
ViewGroup dispatchTouchEvent—>0
ViewGroup onInterceptTouchEvent—>0
View dispatchTouchEvent—>0
View onTouch–>0
View onTouchEvent—>0
ViewGroup dispatchTouchEvent—>2
ViewGroup onInterceptTouchEvent—>2
View dispatchTouchEvent—>2
View onTouch–>2
View onTouchEvent—>2
ViewGroup dispatchTouchEvent—>1
ViewGroup onInterceptTouchEvent—>1
View dispatchTouchEvent—>1
View onTouch–>1
View onTouchEvent—>1
即正常情況下:
- 第一次DOWN
ViewGroup.dispatchTouchEvent ->ViewGroup.onInterceptTouchEvent -> View.dispatchTouchEvent ->View.onTouch -> View.onTouchEvent
- 第二次MOVE
ViewGroup.dispatchTouchEvent -> ViewGroup onInterceptTouchEvent -> View.dispatchTouchEvent -> View.onTouch ->View.onTouchEvent
- 第三次UP
ViewGroup.dispatchTouchEvent -> ViewGroup onInterceptTouchEvent -> View.onTouch -> View.onTouchEvent -> View.onclick
第二種情況:onClick 沒有 理解為沒有消費事件,即沒有進入onClick方法不返回true
ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent -> View.dispatchTouchEvent -> View.onTouch -> View onTouchEvent -> ViewGroup.onTouchEvent
第三種情況:在 View 的 onTouchEvent() 方法裡面返回true 的情況下
第一次DOWN ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent -> View.dispatchTouchEvent -> View.onTouch -> View.onTouchEvent
第二次MOVE ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent -> View.dispatchTouchEvent -> View.onTouch -> View.onTouchEvent
第三次UP ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent -> View.onTouch -> View.onTouchEvent
第四種情況:在 ViewGroup 的 onInterceptTouchEvent() 方法裡面返回 true 的情況下
ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent -> ViewGroup.onTouchEvent
總結
1.如果說子 View 沒有一個地方返回 true ,只會進來一次只會響應 DOWN 事件,代表不需要消費該事件,如果你想響應 MOVE,UP 必須找個地方ture
2.對於ViewGroup來講,如果你想攔截子 View 的 Touch 事件,可以覆寫 onInterceptTouchEvent 返回 true 即可 , 如果說 onInterceptTouchEvent 返回的是 true 會執行該 ViewGroup 的 onTouchEvent 方法 , 如果子 View 沒有消費 touch 事件也會呼叫該 ViewGroup 的 onTouchEvent 方法
相關文章
- Android View 事件分發原始碼分析AndroidView事件原始碼
- Android事件分發:從原始碼角度分析View事件分發機制Android事件原始碼View
- ViewGroup事件分發和處理原始碼分析View事件原始碼
- 自定義view原始碼分析之事件分發01View原始碼事件
- vscode原始碼分析【五】事件分發機制VSCode原始碼事件
- Flutter事件分發原始碼剖析Flutter事件原始碼
- 基於原始碼分析 Android View 事件分發機制原始碼AndroidView事件
- 從原始碼看 Android 事件分發原始碼Android事件
- 事件分發機制(二):原始碼篇事件原始碼
- Android事件分發原始碼歸納Android事件原始碼
- Android原始碼角度分析事件分發消費(徹底整明白Android事件)Android原始碼事件
- Android原始碼角度分析事件分發消費之應用篇Android原始碼事件
- 自定義控制元件(二) 從原始碼分析事件分發機制控制元件原始碼事件
- Android系統原始碼剖析-事件分發Android原始碼事件
- Android 事件分發機制原始碼解析Android事件原始碼
- 【Android原始碼】View的事件分發機制Android原始碼View事件
- Android 事件分發機制原始碼解析-view層Android事件原始碼View
- View事件分發機制分析View事件
- React原始碼分析 – 事件機制React原始碼事件
- Flutter事件響應原始碼分析Flutter事件原始碼
- React原始碼分析 - 事件機制React原始碼事件
- 從點選螢幕到事件處理的事件分發原始碼流程事件原始碼
- Android 事件分發機制原始碼詳解-最新 APIAndroid事件原始碼API
- Android從原始碼角度剖析View事件分發機制Android原始碼View事件
- Android事件分發機制之原始碼完美解析(上)Android事件原始碼
- flutter原始碼系列 PageView原始碼分析以及監聽事件Flutter原始碼View事件
- RecyclerView 事件分發原理實戰分析View事件
- View的事件分發機制分析View事件
- SOFA 原始碼分析— 事件匯流排原始碼事件
- 圖解Android事件分發機制(深入底層原始碼)圖解Android事件原始碼
- redis個人原始碼分析筆記3---redis的事件驅動原始碼分析Redis原始碼筆記事件
- Android系統原始碼分析-事件收集Android原始碼事件
- SpringBoot事件監聽器原始碼分析Spring Boot事件原始碼
- Fabric 1.0原始碼分析(13)events(事件服務)原始碼事件
- Flutter 事件分發Flutter事件
- Android事件分發機制本質是樹的深度遍歷(圖+原始碼)Android事件原始碼
- IOS免籤封包分發原始碼iOS原始碼
- Retrofit原始碼分析三 原始碼分析原始碼