跳到主要内容

布局系统

Android 的布局系统定义了用户界面的结构。通过布局文件,我们可以声明式地定义界面元素及其排列方式,实现各种复杂的界面效果。

布局基础

视图与视图组

Android 的 UI 由 View 和 ViewGroup 两类对象构建:

  • View(视图):所有 UI 组件的基类,如 TextView、Button、ImageView 等
  • ViewGroup(视图组):继承自 View,可以包含其他 View,用于组织布局

XML 布局文件

布局文件存放在 res/layout/ 目录下,使用 XML 格式描述界面结构:

<?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">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World" />

</LinearLayout>

尺寸单位

Android 支持多种尺寸单位:

单位说明使用场景
dp (dip)密度无关像素,推荐使用大部分尺寸定义
sp缩放无关像素,随字体设置缩放字体大小
px实际像素,不推荐特殊需求
pt点,1pt = 1/72 英寸打印相关
mm毫米特殊需求
in英寸特殊需求

宽高属性

每个视图必须定义 layout_widthlayout_height

  • match_parent:匹配父容器的大小
  • wrap_content:刚好包裹内容
  • 具体数值:如 100dp
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="宽度填满,高度自适应" />

<View
android:layout_width="100dp"
android:layout_height="100dp" />

常用布局容器

LinearLayout(线性布局)

LinearLayout 按照水平或垂直方向依次排列子视图。

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="第一个" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="第二个"
android:layout_marginTop="8dp" />

</LinearLayout>

关键属性:

属性说明
orientation排列方向horizontal, vertical
gravity子视图的对齐方式center, top, bottom, left, right 等
layout_gravity当前视图在父容器中的对齐方式同上
layout_weight权重,分配剩余空间数值

权重示例:

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="按钮1" />

<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="按钮2" />

</LinearLayout>

权重为 1:2,按钮2 的宽度是按钮1 的两倍。

ConstraintLayout(约束布局)

ConstraintLayout 是 Android 推荐的现代布局,功能强大,可以创建复杂的扁平化布局,减少嵌套层级。

<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="标题"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

<Button
android:id="@+id/btn_confirm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="确认"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/btn_cancel"
android:layout_marginTop="16dp" />

<Button
android:id="@+id/btn_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="取消"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toEndOf="@id/btn_confirm"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

常用约束属性:

属性说明
layout_constraintTop_toTopOf顶部对齐到目标顶部
layout_constraintTop_toBottomOf顶部对齐到目标底部
layout_constraintBottom_toTopOf底部对齐到目标顶部
layout_constraintBottom_toBottomOf底部对齐到目标底部
layout_constraintStart_toStartOf起始边对齐到目标起始边
layout_constraintStart_toEndOf起始边对齐到目标结束边
layout_constraintEnd_toStartOf结束边对齐到目标起始边
layout_constraintEnd_toEndOf结束边对齐到目标结束边

居中和填充:

<!-- 水平和垂直居中 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="居中显示"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

<!-- 填充整个父容器 -->
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#FF5722"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

Bias(偏移):

当视图在两个方向都有约束时,可以使用 bias 控制偏移:

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="偏移 30%"
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

Guideline(辅助线):

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="对齐到辅助线"
app:layout_constraintStart_toStartOf="@id/guideline" />

</androidx.constraintlayout.widget.ConstraintLayout>

FrameLayout(帧布局)

FrameLayout 将所有子视图叠加在一起,后添加的视图在上层。适合显示单个视图或叠加效果。

<FrameLayout
android:layout_width="match_parent"
android:layout_height="200dp">

<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/background"
android:scaleType="centerCrop" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="叠加文字"
android:layout_gravity="center"
android:textColor="#FFFFFF"
android:textSize="24sp" />

</FrameLayout>

RelativeLayout(相对布局)

RelativeLayout 允许子视图相对于父容器或其他子视图定位。虽然功能强大,但推荐使用 ConstraintLayout 替代。

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="标题"
android:layout_centerHorizontal="true" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="在标题下方"
android:layout_below="@id/tv_title"
android:layout_centerHorizontal="true" />

</RelativeLayout>

内边距与外边距

padding(内边距)

内边距是视图内容与边框之间的距离:

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="内容"
android:padding="16dp" <!-- 四边相同 -->
android:paddingTop="8dp" <!-- 上 -->
android:paddingBottom="8dp" <!-- 下 -->
android:paddingStart="16dp" <!-- 起始边 -->
android:paddingEnd="16dp" /> <!-- 结束边 -->

margin(外边距)

外边距是视图与其他视图之间的距离:

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="内容"
android:layout_margin="16dp" <!-- 四边相同 -->
android:layout_marginTop="8dp" <!-- 上 -->
android:layout_marginBottom="8dp" <!-- 下 -->
android:layout_marginStart="16dp" <!-- 起始边 -->
android:layout_marginEnd="16dp" /> <!-- 结束边 -->

在代码中操作布局

获取视图引用

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

// 传统方式
val textView = findViewById<TextView>(R.id.tv_title)
textView.text = "新标题"

// ViewBinding(推荐)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.tvTitle.text = "新标题"
}
}

动态添加视图

val linearLayout = findViewById<LinearLayout>(R.id.container)

val textView = TextView(this).apply {
text = "动态添加的文本"
textSize = 16f
setPadding(16, 16, 16, 16)
}

val params = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
linearLayout.addView(textView, params)

修改布局参数

val textView = findViewById<TextView>(R.id.tv_title)

// 修改宽高
val params = textView.layoutParams
params.width = 200
params.height = 100
textView.layoutParams = params

// 或使用 ConstraintLayout.LayoutParams
val constraintParams = textView.layoutParams as ConstraintLayout.LayoutParams
constraintParams.topToBottom = R.id.header
textView.layoutParams = constraintParams

布局优化

减少布局层级

过多的嵌套会影响性能,应尽量使用 ConstraintLayout 减少层级。

<!-- 不推荐:多层嵌套 -->
<LinearLayout>
<LinearLayout>
<LinearLayout>
<TextView />
</LinearLayout>
</LinearLayout>
</LinearLayout>

<!-- 推荐:扁平化布局 -->
<ConstraintLayout>
<TextView />
</ConstraintLayout>

使用 include 复用布局

<!-- res/layout/item_header.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="标题" />

</LinearLayout>

<!-- 使用 include 引入 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<include layout="@layout/item_header" />

</LinearLayout>

使用 ViewStub 延迟加载

ViewStub 是一个轻量级的占位视图,只有在需要时才加载实际布局。

<ViewStub
android:id="@+id/stub_progress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/progress_overlay" />
// 需要时加载
val stub = findViewById<ViewStub>(R.id.stub_progress)
val inflatedView = stub.inflate()

// 或设置为可见时自动加载
stub.visibility = View.VISIBLE

使用 merge 标签

当根布局是 FrameLayout 或不需要根容器时,使用 merge 减少层级:

<!-- res/layout/item_merge.xml -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="文本1" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="文本2" />

</merge>

ViewBinding

ViewBinding 是 Android 官方推荐的视图绑定方案,可以替代 findViewById,提供类型安全和空安全保证。

启用 ViewBinding

在模块的 build.gradle.kts 中添加:

android {
viewBinding {
enable = 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.tvTitle.text = "Hello"
binding.btnSubmit.setOnClickListener {
// 处理点击
}
}
}

在 Fragment 中使用

class MyFragment : Fragment() {

private var _binding: FragmentMyBinding? = null
private val binding get() = _binding!!

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentMyBinding.inflate(inflater, container, false)
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

binding.tvTitle.text = "Fragment 内容"
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null // 避免内存泄漏
}
}

小结

本章介绍了 Android 布局系统的核心内容:

  1. 布局基础:View、ViewGroup、尺寸单位、宽高属性
  2. 常用布局容器:LinearLayout、ConstraintLayout、FrameLayout、RelativeLayout
  3. 边距处理:padding 和 margin 的使用
  4. 代码操作:动态添加视图、修改布局参数
  5. 布局优化:减少层级、include、ViewStub、merge
  6. ViewBinding:类型安全的视图绑定

下一章将学习常用的 UI 组件,如 TextView、Button、ImageView 等。