findViewById可以说是学习Android开发中最常用的方法了,这里我们就来了解一下这个方法。首先从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方法的实现,所以要求他的父类中看: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实现:
@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方法实现,但是他是继承于FrameLayout的,可以去看看,同样FrameLayout也没有实现,FrameLayout又是继承于ViewGroup,不过ViewGroup也没有实现,那就看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;
}
发现并没有什么用,findViewTraversal中只是将id和自己的id比较,是的话返回自己,否则返回空。看似陷入死胡同了,其实想想也是这样,毕竟VIew代表一个单独控件,没有孩子,寻找一个id最多只能找到自己。所以要去那些有孩子的类中找,既然是从DecorView 过来的,他们又都没有重写findViewById,那么就取父类中找,发现只有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的完整实现了,就是遍历viewgroup的所有孩子,然后递归调用findViewById,知道找到对应的view为止。我们若是在activity中定义,则是从顶层视图开始搜索。