ViewGroup是如何获取到xml中child设置的对应的属性的?

485 阅读3分钟

以常规的Activity启动开始,我们追一下详细的调用栈。

android sdk版本是30。

Activity#setContent方法中的布局如何生成View对象的:


--> ActivityThread#handleLaunchActivity

--> ActivityThread#performLaunchActivity

--> Instrumentation#callActivityOnCreate

--> Activity#performCreate(Bundle icicle)

--> Activity#performCreate(Bundle icicle, PersistableBundle persistentState)

--> Activity#onCreate(Bundle savedInstanceState)

--> AppCompatActivity#setContentView(@LayoutRes int layoutResID)

--> AppCompatDelegateImpl#setContentView(int resId)

------AppCompatDelegateImpl#ensureSubDecor():初始化好DecorView------> PhoneWindow#getDecorView()

------> PhoneWindow#installDecor()

------> PhoneWindow#generateLayout(DecorView decor)

------> DecorView#onResourcesLoaded(LayoutInflater inflater, int layoutResource):返回一个View------> LayoutInflate#inflate(@LayoutRes int resource, @Nullable ViewGroup root)

----------先调用LayoutInflate#tryInflatePrecompiled方法,尝试获取View。默认mUseCompiledViewfalse,直接返回null----------上面方法获取不到时,接着获取XmlResourceParser,通过LayoutInflate#inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)方法获取View------② 调用LayoutInflater.from(mContext).inflate(resId, contentParent):将Activity#setContentView方法中设置的资源文件生成对应的View并添加到contentParent中。

LayoutInflater#inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)方法中:

AttributeSet attrs = Xml.asAttributeSet(parser):从XmlPullParser中获取到AttributeSet

②通过advanceToRootNode(parser)方法,找到START_TAG后面的第一个元素;通过parser.getName()获取到name——"LinearLayout"

③通过LayoutInflater#createViewFromTag(View parent, String name, Context context, AttributeSet attrs)获取View

——> LayoutInflater#createViewFromTag(View parent, String name, Context context, AttributeSet attrs,boolean ignoreThemeAttr)

----调用LayoutInflater#tryCreateView方法:尝试创建View。本次返回null--------先使用mFactory2#onCreateView方法,调用到AppCompatViewInflater#createView方法中,这里做一些hook,将TextViewImageViewButtonEditTextCheckBox等基础组件,替换成AppCompat系列组件。不过没有处理LinearLayout,所以返回viewnull--------如果viewnull,接着调用mPrivateFactory.onCreateView方法,这里会先调用FragmentActivity#dispatchFragmentsOnCreateView方法,看是否是"fragment"标签;如果不是,就调用Activity#onCreateView方法进行传递。这里也返回为null----如果tryCreateView创建View失败,则接着调用LayoutInflater#onCreateView(@NonNull Context viewContext, @Nullable View parent, @NonNull String name, @Nullable AttributeSet attrs)方法;

------>调用LayoutInflater#onCreateView(View parent, String name, AttributeSet attrs);

------>调用LayoutInflater#onCreateView(String name, AttributeSet attrs)方法;设置prefix为"android.view."。

------>调用LayoutInflater#createView(String name, String prefix, AttributeSet attrs)

------>调用LayoutInflater#createView(Context viewContext, String name, String prefix, AttributeSet attrs):

----------通过prefixname获取Constructor对象。

----------构建Object[] args参数,依次传入上下文、attrs,然后通过反射生成LinearLayout的对象,调用的是LinearLayout(Context context, AttributeSet attrs)构造方法。

----------返回生成的View对象。

④如果root != null,就调用root.generateLayoutParams方法生成LayoutParams;如果attachToRootfalse,就将LayoutParams设置给③生成的View。

⑤调用LayoutInflater#rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs, boolean finishInflate):生成child对象

--> LayoutInflater#rInflate(XmlPullParser parser, View parent, Context context, AttributeSet attrs, boolean finishInflate)

---- 开启while循环:((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT

----------通过parser.getName()获取name,针对"requestFocus"、"tag"、"include"、"merge"和else情况做不同的处理。

----------else情况下:

--------------通过LayoutInflater#createViewFromTag(View parent, String name, Context context, AttributeSet attrs)获取View--------------通过parent#generateLayoutParams方法,从xml中读取parent对应的数据,生成对应的LayoutParams数据,包括基础的layout_widthlayout_height,一个给个ViewGroup自定义的LayoutParams数据。

--------------递归调用LayoutInflater#rInflateChildren,对Viewchild创建。

--------------通过ViewGroup#addView(View child, LayoutParams params)方法,将child添加进parent。

⑥如果root != null && attachToRoot,通过ViewGroup#addView(View child, LayoutParams params)方法,将child和④生成的LayoutParams数据,添加进root。

⑦如果root == null || !attachToRoot,将temp赋值给result

结论:

在从xml文件变成View对象,并添加到View树的过程中,必然会调用parent#generateLayoutParams方法。

ViewGroup#generateLayoutParams方法中,会读取layout_widthlayout_height基础属性。

由于各个控件都是继承自ViewGroup的,他们一般会继承ViewGroup.LayoutParams,并据此重写自己的generateLayoutParams方法,在generateLayoutParams方法中一般都会从child的属性中读取自己关注的属性,所以此时写在child中的xml属性会被读取,从而生效。