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

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

以常规的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。默认mUseCompiledView为false,直接返回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,将TextView、ImageView、Button、EditText、CheckBox等基础组件,替换成AppCompat系列组件。不过没有处理LinearLayout,所以返回view为null。

--------如果view为null,接着调用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):

----------通过prefix和name获取Constructor对象。

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

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

④如果root != null,就调用root.generateLayoutParams方法生成LayoutParams;如果attachToRoot为false,就将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_width和layout_height,一个给个ViewGroup自定义的LayoutParams数据。

--------------递归调用LayoutInflater#rInflateChildren,对View的child创建。

--------------通过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属性会被读取,从而生效。

相关文章
|
2月前
|
XML JavaScript 前端开发
XML DOM - 属性和方法
XML DOM通过属性(如nodeName、nodeValue、parentNode、childNodes和attributes)和方法提供编程接口,让开发者用JavaScript等语言以节点方式交互XML。属性描述节点信息,方法执行操作,如删除节点。
|
2月前
|
XML JavaScript 前端开发
属性和方法向 XML DOM 定义了编程接口
XML DOM 提供编程接口,通过属性和方法操作XML结构。使用JavaScript等语言,可访问和修改节点。属性如nodeName、nodeValue揭示节点信息,方法如getElementsByTagName、appendChild、removeChild实现查找、添加和删除节点功能。节点对象x的应用示例贯穿其中。
|
2月前
|
XML JavaScript 前端开发
XML DOM - 属性和方法
**XML DOM 提供编程接口,将XML转换为节点对象,便于通过JS等语言操作。属性如 nodeName、nodeValue、parentNode、childNodes 和 attributes 描述节点详情。方法用于执行操作,如删除节点。**
|
1月前
|
XML JavaScript 前端开发
属性和方法向 XML DOM 定义了编程接口
XML DOM 是一个编程接口,它将XML表示为节点对象集合,可通过JavaScript等语言访问。接口通过属性和方法定义,属性如nodeName、nodeValue显示节点信息,方法如getElementsByTagName、appendChild、removeChild执行操作。例如,x.nodeName返回节点名称,x.appendChild(node)添加子节点。
|
2月前
|
XML JavaScript 前端开发
XML DOM - 属性和方法
XML DOM 提供编程接口,让开发者用JavaScript等语言操作XML文档。接口包含属性和方法,属性如nodeName、nodeValue、parentNode和childNodes,用于查询节点信息;方法如getElementsByTagName、appendChild和removeChild,执行增删操作。示例中,JavaScript代码`txt=xmlDoc.getElementsByTagName("title")[0].childNodes[0].nodeValue`从books.xml获取第一个<title>元素的文本内容,赋值给变量txt。
|
2月前
|
XML JavaScript 数据格式
python - bs4提取XML/HTML中某个标签下的属性
python - bs4提取XML/HTML中某个标签下的属性
36 0
|
2月前
|
XML Java 数据格式
spring通过文件属性注入bean和基于xml的bean的自动装配以及spring-eel表达式的使用加代码合集
spring通过文件属性注入bean和基于xml的bean的自动装配以及spring-eel表达式的使用加代码合集
57 0
|
9月前
|
XML 数据管理 数据处理
XML入门,之dtd属性与元素详解
XML入门,之dtd属性与元素详解
31 1
|
9月前
|
JavaScript 前端开发 安全
12dwr - dwr.xml配置(allow标签-Converter属性)
12dwr - dwr.xml配置(allow标签-Converter属性)
29 0
|
9月前
|
开发框架 JavaScript 前端开发
11dwr - dwr.xml配置(allow标签-Creator属性)
11dwr - dwr.xml配置(allow标签-Creator属性)
39 0