zjy519893509的专栏

findViewById原理

前言

从表面上来看,findViewById就是根据R文件中的id值查询到相应的View,然后返回。那么问题来了,这些View是在find的时候才被实例化还是父View实例化好的时候就已经实例化好了,findViewById只是从数据源中取出来。答案是在父类被实例化好的时候就已经完成了子类的实例化,并且形成了一个DOM树。这个DOM数其实就是一个View数组

所以为了理解findViewById的原理还要从一个Activity的实例化开始讲解。

如何初始化所有的View对象

1.在Activity的onCreate方法中有一句

setContentView(R.layout.activity_main);

这一句代码将布局文件的id作为参数传入

2.然后在PhoneWindow类中的public void setContentView(int layoutResID)开始了实例化。在这个方法中核心代码是

private LayoutInflater mLayoutInflater;……mLayoutInflater.inflate(layoutResID, mContentParent);

3.最后在LayoutInflater 中的

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {synchronized (mConstructorArgs) {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");final AttributeSet attrs = Xml.asAttributeSet(parser);Context lastContext = (Context)mConstructorArgs[0];mConstructorArgs[0] = mContext;View result = root;……//按标签创建子Viewfinal View temp = createViewFromTag(root, name, attrs, false);……// Inflate all children under temp//实例化它所有的子ViewrInflate(parser, temp, attrs, true, true);……(root != null && attachToRoot) {root.addView(temp, params);}(root == null || !attachToRoot) {result = temp;}}……return result;}}

这个方法会讲XML解析生成一个View对象然后addView到根节点里

4.而在addView 最终会调用ViewGroup中是

index) {View[] children = mChildren;final int count = mChildrenCount;final int size = children.length;if (index == count) {if (size == count) {//扩充数组的大小mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];System.arraycopy(children, 0, mChildren, 0, size);children = mChildren;}children[mChildrenCount++] = child;} {if (size == count) {mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];System.arraycopy(children, 0, mChildren, 0, index);System.arraycopy(children, index, mChildren, index + 1, count – index);children = mChildren;} else {System.arraycopy(children, index, children, index + 1, count – index);}children[index] = child;mChildrenCount++;if (mLastTouchDownIndex >= index) {mLastTouchDownIndex++;}} else {throw new IndexOutOfBoundsException("index=" + index + " count=" + count);}}

简单是说就是生成了一个View数组

findViewById的工作原理

那么findViewById是怎么工作的了? 我们沿着继承树向上找.1.Activity中

id) {return getWindow().findViewById(id);}

2.getWindow:

{return mWindow;}

mWindow 是 private Window mWindow; 是一个Window类型的变量

3.进入Window类,在Window中查找findViewById:

id) {return getDecorView().findViewById(id);}

4.发现getDecorView()是Window中的一个抽象方法。而Window唯一的子类是PhoneWindow.

5.在PhoneWindow找到getDecorView()方法

{if (mDecor == null) {installDecor();}return mDecor;}

而mDecor是Phone的一个内部类DecorView

它继承了FrameLayout ,而FrameLayout 是ViewGroup的子类

6.最终我们在ViewGroup中找到了执行findViewById真正的主体方法。

id) {if (id == mID) {return 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 v;}}}return null;}

该方法意思就是遍历我们前面已经加载好的View数组对每个view执行findViewById,这个findViewById是在View中被实现的。它会调用findViewTraversal来辨别寻找的id与找到的id是否相等

id) {if (id == mID) {return this;}return null;}

而这个mID的值是在View被初始化时就已经被赋值了。通过遍历如果id存在,就能成功找到View

总结每个人在他的人生发轫之初,总有一段时光,

zjy519893509的专栏

相关文章:

你感兴趣的文章:

标签云: