角色
我们知道 Android 的布局主要有两种来源:
- 代码创建 :直接
new TextView(context)
等方式构建。 - XML 创建 :在
res/layout
下定义.xml
文件,然后通过setContentView(R.layout.xxx)
或LayoutInflater
来加载。
LayoutInflater
就是桥梁,它的核心工作是:
- 读取 XML 布局文件。
- 解析节点(如
LinearLayout
,TextView
)。 - 使用反射调用构造方法生成对应的 View。
- 处理属性(如
android:layout_width
、android:text
)。 - 组装成 View 树,返回根节点 View 实例。
调用链
常见的调用方式有三种:
在 Activity 中
java
setContentView(R.layout.activity_main);
实际上内部会走到:
java
LayoutInflater.from(this).inflate(R.layout.activity_main, getWindow().getDecorView(), true);
手动调用
java
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.item_view, parent, false);
在 Adapter 中
比如 RecyclerView 的 onCreateViewHolder
:
java
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_list, parent, false);
return new MyViewHolder(itemView);
}
参数含义
java
inflate(int resource, ViewGroup root, boolean attachToRoot)
resource
:要解析的布局文件 ID。root
:父容器。attachToRoot
:是否将生成的 View 立即添加到 root 中。
关键点
- 如果
root != null
且attachToRoot = true
,生成的 View 会自动加入 root。 - 如果
attachToRoot = false
,返回的 View 不会被加进去,但会用 root 的LayoutParams
。 - 如果
root = null
,则只能拿到 View,本身没有 LayoutParams。
实际开发中,Adapter 场景常用 inflate(R.layout.xxx, parent, false)
,因为 Adapter 本身会先进行数据处理,再将处理过的 item 控件加入 root ,attachToRoot = false 避免重复 attach。
源码
inflate()
的核心片段:
java
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
View result = createViewFromTag(...); // 反射生成View
ViewGroup.LayoutParams params = null;
if (root != null) {
params = root.generateLayoutParams(attrs); // 生成布局参数
if (attachToRoot) {
root.addView(result, params); // 加入父布局
}
}
if (root != null && !attachToRoot) {
result.setLayoutParams(params); // 仅赋值,不加入
}
return result;
}
可以看到核心流程就是: 反射创建 View → 解析属性 → 生成 LayoutParams → 根据 attachToRoot 决定是否加入父容器。
LayoutParams
LayoutParams 是用于父布局确定子控件在其内部的位置,大小或权重等属性的类,父布局下每个子控件都有自己的 LayoutParams 实例,由父布局创建和调用且只在父布局内生效,涉及 margin ,layout_gravity 等属性。
padding ,gravity 等子控件本身的属性在控件实例构造时完成,由 View 自己负责渲染加载和赋值,不涉及 LayoutParams 的父加载。
场景实践
- Activity 场景 :直接用
setContentView
。 - Fragment 场景 :
inflater.inflate(R.layout.xxx, container, false)
。 - Adapter 场景 :
inflater.inflate(R.layout.item, parent, false)
。 - 避免误用:attachToRoot 几乎只在特殊场景用 true。
- 优化 :复杂布局中可借助
<merge>
、ViewStub
来减少层级,提升性能。
LayoutInflater
是 Android 界面系统的核心工具之一,看似只是 inflate 一行代码,实际上内部涉及反射、XML 解析、View 树构建等复杂逻辑。掌握它的参数意义和调用链,能写出高效可维护的 UI 代码。
问题
在一个新的XML布局文件中自己定义一个按钮,将这个按钮通过 LayoutInflater 膨胀到主页面 activity_main
上,给出代码
所以答案如下
xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</FrameLayout>
java
LinearLayout container = findViewById(R.id.main);
container.addView(getLayoutInflater().inflate(R.layout.button_layout, container, false));
// or
getLayoutInflater().inflate(R.layout.button_layout, container, true);