1.AndroidStudio原始碼除錯方式
AndroidStudio默認是支持一部分原始碼除錯的,但是build.gradle(app) 中的sdk版本要保持一致,
最好是編譯版本、運行版本以及手機的版本都保持一致,比如
android {
compileSdkVersion 30 //1
buildToolsVersion "30.0.0"
defaultConfig {
applicationId "komine.demos.app"
minSdkVersion 26
targetSdkVersion 30 //2
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}
設定斷點的原始碼注意也要一致,比如設定的Android SDK 30,原始碼也要選擇對應的版本.我的AndroidStudio版本是3.6.3
如果都一致發現斷點的位置還是不正確,可以重置快取,在 File-->Invalidate Caches 重新構建.
運行程式之后發現斷點上有一個綠色的對勾,表示該斷點也可以被執行,并不是100%,所以關鍵代碼除錯,最好步進除錯.
如果斷點成功執行了,如下圖

2.ViewGroup事件分發原始碼分析
當我們在螢屏觸摸的時候,不管你觸摸到那里,是View上面,還是一個View的沒有的Activity上,都會呼叫Activity的dispatchTouchEvent,如果要觀察touchEvent事件分發程序,
可以自己定義一個ViewGroup,和View,在其中列印日志
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_
android:layout_height="match_parent">
<komine.demos.app.TouchLayout
android:layout_
android:layout_height="match_parent">
<komine.demos.app.TouchView
android:layout_
android:layout_height="200dp"
android:background="#39c5bb"/>
<komine.demos.app.TouchView
android:layout_
android:layout_height="200dp"
android:background="#39c5bb"/>
</komine.demos.app.TouchLayout>
</LinearLayout>
所有Touch事件都會進到Activity.dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//如果是down事件,則會回呼Activity的onUserInteraction()方法
//我們可以在該方法中做一些其他事情
onUserInteraction();
}
//getWindow()是mWindow,在Activity的attach方法中賦值,是一個PhoneWindow物件,是直接new PhoneWindow
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//如果事件沒有被消費,則會回呼Activity的onTouchEvent()事件
return onTouchEvent(ev);
}
PhoneWindow.superDispatchTouchEvent
如果你想知道mDecor是干什么的或者怎么被創建的,可以看我的另一篇博客
setContentView原始碼分析
我們直接把它當作ViewGroup看待就行了
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
//superDispatchTouchEvent() 其實是呼叫了ViewGroup的dispatchTouchEvent,
//因為mDecor是繼承自ViewGroup
return mDecor.superDispatchTouchEvent(event);
}
重點來了,ViewGroup.dispatchTouchEvent
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
...
//函式的回傳值,表示當前事件是否有View消費,true表示有View消費該事件
//則其他View就不能再處理該事件了
boolean handled = false;
//View的方法,默認回傳true
if (onFilterTouchEventForSecurity(ev)) {
...
...
//判斷當前ViewGroup是否攔截該事件,
final boolean intercepted;
//down事件才判斷是否攔截,所以ViewGroup要攔截事件,一定要在
//down事件的時候攔截,其他事件會被忽略
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//FLAG_DISALLOW_INTERCEPT 是在requestDisallowInterceptTouchEvent()中賦值的
//表示子View是否請求了父ViewGroup不攔截本次事件,非ViewGroup子View可以呼叫getParent().requestDisallowInterceptTouchEvent()
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//如果子View沒有請求父ViewGroup不攔截自己
if (!disallowIntercept) {
//呼叫當前ViewGroup的onInterceptTouchEvent()看看自己是否攔截
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
...
...
//caceled默認回傳false, intercepted表示當前ViewGroup是否要攔截事件
if (!canceled && !intercepted){
...
...
//這是一個倒序回圈,最后的那個view會先進行事件分發
for (int i = childrenCount - 1; i >= 0; i--) {
...
...
//見下方,dispatchTransformedTouchEvent()對子view進行事件分發
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
//如果有子View回傳true,中斷事件分發,break完成子View的事件分發
}
}
}
}
//mFirstTouchTarget == null 說明沒有子View消費當前事件,則會呼叫
//父view的dispatchTouchEvent(),完成當前ViewGroup的事件分發
//意思就是,我已經分完了,我的子View沒人要消費事件,你看你要不要,把事件分發交給
//父類去處理,然后父類又會重復上述步驟,進行事件分發
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
...
handled = true;
}
//所有ViewGroup分發完成之后的最終結果
return handled;
}
dispatchTransformedTouchEvent
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
...
...
//判斷是否是同一根手指
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
//如果子View為空,則回傳父類的dispatchTouchEvent()結果
handled = super.dispatchTouchEvent(event);
} else {
...
//呼叫子View的dispatchTouchEvent(),子View的dispatchTouchEvent()又會呼叫onTouchEvent(),
//如果View設定了TouchListener則會優先以TouchListener結果回傳,然后才會以onTouchEvent()的結果
//作為回傳值,這邊不貼代碼了,可以自己去看看
handled = child.dispatchTouchEvent(event);
...
}
return handled;
}
}
}
Touch事件分發流程:

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/458161.html
標籤:Android
