View 与 ViewGroup
本教程共 100 篇 · 第 57 篇 · 更新于 2026-07-28 · 约 7 分钟阅读
57. View 与 ViewGroup
本节目标:理解传统 View 体系的核心结构,学会用 XML 写布局、用 ViewBinding 替代 findViewById 访问控件。
View 体系是什么
前面十几章我们一直在学 Compose,它是 Android 的现代 UI 方案。但 Compose 之前,Android 用了十几年的 UI 体系就是 View 体系。即使你的新项目全用 Compose,你也可能遇到老代码、第三方库、需要嵌入的地图或广告组件,这些都基于 View 体系。
打个比方:Compose 是新盖的高楼,View 体系是旁边的老街区。你住新楼,但偶尔得去老街办事,所以得懂那边的路。
View 与 ViewGroup
传统 UI 由两类对象组成:
- View(视图):可见的界面元素,能绘制内容、处理交互。比如
TextView、Button、ImageView。 - ViewGroup(视图组):不可见的容器,负责排列子 View。比如
LinearLayout、ConstraintLayout。
它们形成一棵树:
ViewGroup (根布局)
├── View (TextView)
├── ViewGroup (LinearLayout)
│ ├── View (Button)
│ └── View (ImageView)
└── View (TextView)
这和 Compose 的组件树概念一样,只是实现方式不同。
XML 布局
传统 View 用 XML 描述界面结构。每个 XML 标签对应一个 View 或 ViewGroup:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="标题"
android:textSize="20sp" />
<Button
android:id="@+id/submit_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="提交" />
</LinearLayout>
尺寸属性
| 值 | 含义 |
|---|---|
match_parent | 填满父容器 |
wrap_content | 包裹内容 |
100dp | 固定尺寸 |
id 标识
android:id="@+id/title" 给控件分配唯一 id。@+id 表示新建 id,@id 表示引用已有 id。代码里通过这个 id 找到控件。
在 Activity 中加载布局
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) // 加载 XML 布局
}
}
R.layout.activity_main 对应 res/layout/activity_main.xml。
findViewById(老方式)
传统方式用 findViewById 拿到控件实例:
val title = findViewById<TextView>(R.id.title)
val button = findViewById<Button>(R.id.submit_button)
button.setOnClickListener {
title.text = "已点击"
}
Warning
findViewById有几个问题:类型不安全(可能强转出错)、id 拼错运行时才报、代码冗长。现在基本不用了,用 ViewBinding 替代。
ViewBinding(推荐方式)
ViewBinding 自动生成一个绑定类,让你类型安全地访问所有控件。
开启 ViewBinding
模块级 build.gradle.kts:
android {
buildFeatures {
viewBinding = true
}
}
在 Activity 中使用
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 直接通过 binding 访问控件,类型安全
binding.title.text = "标题"
binding.submitButton.setOnClickListener {
binding.title.text = "已点击"
}
}
}
规则:XML 文件名 activity_main.xml 对应绑定类 ActivityMainBinding。控件的 id submit_button 变成驼峰 submitButton。
Tip
- ViewBinding 的好处:编译期检查(id 写错编译就报错)、类型安全(不用强转)、空安全(
@Nullable标注的控件返回可空类型)。新项目用传统 View 时务必开启。
在 Fragment 中使用
class HomeFragment : Fragment() {
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null // Fragment 的 View 销毁时置空,防泄漏
}
}
Warning
- Fragment 里用 ViewBinding 要注意:
onDestroyView里必须把 binding 置空。因为 Fragment 的 View 可能被销毁但 Fragment 实例还在,如果 binding 还持有 View 引用会导致内存泄漏。用_binding+binding的 getter 模式是官方推荐写法。
ViewBinding vs DataBinding
| 特性 | ViewBinding | DataBinding |
|---|---|---|
| 功能 | 访问控件 | 访问控件 + 数据绑定 |
| 编译速度 | 快 | 慢(需要处理表达式) |
| 学习成本 | 低 | 高 |
| 推荐度 | 新项目推荐 | 老项目或需要复杂绑定时用 |
Note
- ViewBinding 是 DataBinding 的简化版,只做控件访问,不做数据绑定表达式。如果你的界面逻辑不复杂,ViewBinding 足够了。如果需要 XML 里写表达式绑定数据(如
android:text="@{user.name}"),才用 DataBinding。第 96 章会详细讲 DataBinding。
小结
View 体系是 Android 的传统 UI 方案,View 是可见控件,ViewGroup 是布局容器,XML 描述界面结构。findViewById 已过时,ViewBinding 是访问控件的标准方式:编译期类型安全、空安全。Fragment 里用 ViewBinding 注意 onDestroyView 置空。下一节学习布局管理器。