findViewById可以說是學習Android開發(fā)中最常用的方法了,這里我們就來了解一下這個方法。首先從activity中看:
public <T extends View> T findViewById(@IdRes int id) {
return getWindow().findViewById(id);
}
public Window getWindow() {
return mWindow;
}
mWindow = new PhoneWindow(this, window, activityConfigCallback);
可見最后走的是PhoneWindow中的findViewById方法,進來看一下:
frameworks\base\core\java\com\android\internal\policy\PhoneWindow.java
但是PhoneWindow中并沒有findViewById方法的實現(xiàn),所以要求他的父類中看:Window
android\frameworks\base\core\java\android\view\Window.java
public <T extends View> T findViewById(@IdRes int id) {
return getDecorView().findViewById(id);
}
看到getDecorView應該就明白是獲取的DecorView,他是Activity的頂層視圖,這個方法在子類中也就是PhoneWindow實現(xiàn):
@Override
public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}
private DecorView mDecor;
我們再去DecorView 中看一下
android\frameworks\base\core\java\android\internal\policy\DecorView.java
這里面也沒有findViewById方法實現(xiàn),但是他是繼承于FrameLayout的,可以去看看,同樣FrameLayout也沒有實現(xiàn),F(xiàn)rameLayout又是繼承于ViewGroup,不過ViewGroup也沒有實現(xiàn),那就看ViewGroup的父類:View。終于找到了:
public final <T extends View> T findViewById(@IdRes int id) {
if (id == NO_ID) {
return null;
}
return findViewTraversal(id);
}
protected <T extends View> T findViewTraversal(@IdRes int id) {
if (id == mID) {
return (T) this;
}
return null;
}
發(fā)現(xiàn)并沒有什么用,findViewTraversal中只是將id和自己的id比較,是的話返回自己,否則返回空??此葡萑胨篮?,其實想想也是這樣,畢竟VIew代表一個單獨控件,沒有孩子,尋找一個id最多只能找到自己。所以要去那些有孩子的類中找,既然是從DecorView 過來的,他們又都沒有重寫findViewById,那么就取父類中找,發(fā)現(xiàn)只有ViewGroup中重寫了:
@Override
protected <T extends View> T findViewTraversal(@IdRes int id) {
if (id == mID) {
return (T) this;
}
final View[] where = mChildren;
final int len = mChildrenCount;
for (int i = 0; i < len; i++) {
View v = where[i];
if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
v = v.findViewById(id);
if (v != null) {
return (T) v;
}
}
}
return null;
}
這里就是findViewById的完整實現(xiàn)了,就是遍歷viewgroup的所有孩子,然后遞歸調(diào)用findViewById,知道找到對應的view為止。我們?nèi)羰窃赼ctivity中定義,則是從頂層視圖開始搜索。