5、布局基础:LinearLayout、RelativeLayout、ConstraintLayout、FrameLayout的对比与使用场景

布局,说白了就是Android应用的骨架。你想想看,一个App界面好不好看、流不流畅,布局选型占了很大比重。我这些年带团队做项目,见过太多因为布局选型不当导致的性能问题——卡顿、过度绘制、甚至OOM。嗯,今天咱们就把这四种最常用的布局聊透。

Android 四大布局 LinearLayout 线性排列(水平/垂直) 权重分配空间 适用:简单列表、表单 RelativeLayout 相对定位(兄弟/父容器) 适合复杂相对关系 注意:多次测量性能开销 ConstraintLayout 约束驱动、扁平化 拖拽可视化编辑 推荐:复杂界面首选 FrameLayout 层叠显示(后入在上) 常用于Fragment容器 适用:单子视图、覆盖层 选型建议 ▸ 简单线性排列 → LinearLayout ▸ 复杂相对布局 → ConstraintLayout(扁平化优先) ▸ 层叠/占位 → FrameLayout ▸ 旧项目维护 → RelativeLayout(但新项目不建议)

5.1 LinearLayout:最直观的线性排列

LinearLayout 是我最早接触的布局,也是很多新手的第一选择。它的核心逻辑很简单——所有子视图按水平或垂直方向排成一列。

核心特性:

  • orientation:指定排列方向(horizontal / vertical)
  • layout_weight:按权重分配剩余空间,非常实用
  • gravity / layout_gravity:控制对齐方式

举个例子,一个简单的登录界面:

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

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入用户名"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入密码"
        android:inputType="textPassword"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="登录"/>
</LinearLayout>

我在项目中遇到过一个问题:用 LinearLayout 嵌套了五六层,结果界面卡得不行。你想想看,每一层都要测量两次(onMeasure),嵌套多了就是灾难。所以我的建议是——LinearLayout 适合浅层使用,别超过两层嵌套。

小技巧:用 layout_weight 时,记得把对应方向的 layout_width 或 layout_height 设为 0dp。这样系统不用先测量子视图大小,直接按权重分配,性能更好。

5.2 RelativeLayout:相对定位的灵活性

RelativeLayout 允许子视图相对于父容器或其他兄弟视图定位。比如「A 在 B 的右边」、「C 在父容器的底部居中」。这在实现一些复杂对齐时非常方便。

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

    <ImageView
        android:id="@+id/avatar"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"/>

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/avatar"
        android:layout_marginLeft="12dp"
        android:text="张三"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/name"
        android:layout_toRightOf="@id/avatar"
        android:layout_marginLeft="12dp"
        android:text="在线"
        android:textSize="12sp"/>
</RelativeLayout>

不过,RelativeLayout 有个明显的短板——它需要两次测量才能确定所有子视图的位置。为什么?因为子视图之间可能存在相互依赖,比如 A 在 B 的左边,B 又在 C 的下边,系统得先算一轮再调整。我曾经在一个列表项里用了 RelativeLayout,结果列表滑动时掉帧严重。后来换成 ConstraintLayout,问题就解决了。

注意:如果你的布局层级超过两层,或者子视图超过 10 个,尽量别用 RelativeLayout。它的测量开销会随着子视图数量指数级增长。

5.3 ConstraintLayout:现代开发的王者

ConstraintLayout 是 Google 在 2016 年推出的布局,目的就是解决嵌套问题。它用「约束」来描述子视图之间的位置关系,说白了就是「A 的左边对齐 B 的右边」、「C 的底部在父容器的底部」这种规则。

我个人习惯是:新项目一律用 ConstraintLayout。为什么?因为它能做到完全扁平化——一个 ConstraintLayout 就能搞定以前需要三四层嵌套才能实现的效果。

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

    <ImageView
        android:id="@+id/avatar"
        android:layout_width="48dp"
        android:layout_height="48dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

    <TextView
        android:id="@+id/name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toEndOf="@id/avatar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="@id/avatar"
        android:layout_marginStart="12dp"
        android:text="张三"/>

    <TextView
        android:id="@+id/status"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toEndOf="@id/avatar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/name"
        android:layout_marginStart="12dp"
        android:text="在线"
        android:textSize="12sp"/>
</androidx.constraintlayout.widget.ConstraintLayout>

ConstraintLayout 的优势:

  • 扁平化:减少嵌套层级,提升测量和绘制性能
  • 灵活:支持比例约束、链条、分组(Group)等高级功能
  • 可视化:Android Studio 的 Design 视图可以直接拖拽编辑
  • 自适应:配合 Guidelines 和 Barrier,轻松适配不同屏幕

我记得有一次做电商首页,顶部有搜索栏、分类标签、轮播图、公告栏,如果用 LinearLayout 嵌套,至少得四层。用 ConstraintLayout 一层就搞定了,性能提升非常明显。

避坑指南:我曾经在 ConstraintLayout 里用了太多「wrap_content」,导致约束链计算复杂,反而比 RelativeLayout 还慢。后来我改成用「0dp」(MATCH_CONSTRAINT)配合比例,性能就上来了。记住:能用 0dp 就别用 wrap_content。

5.4 FrameLayout:轻量级的层叠容器

FrameLayout 是最简单的布局——所有子视图都从左上角开始堆叠,后添加的会覆盖在先添加的上面。它只有一个子视图时性能最好,适合做 Fragment 容器、加载占位、或者悬浮按钮这类场景。

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

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_marginBottom="16dp"
        android:text="图片底部文字"
        android:textColor="#fff"
        android:textSize="16sp"/>
</FrameLayout>

FrameLayout 的 layout_gravity 属性可以控制子视图的对齐位置,但注意它不支持相对定位。如果你需要「A 在 B 的右边」,那就别用 FrameLayout,它做不到。

注意:FrameLayout 的子视图如果都设为 match_parent,那只有最后一个能完全显示。我之前见过有人用 FrameLayout 做列表项,里面放了三个 match_parent 的 ImageView,结果只显示了最后一张图。嗯,这种用法显然不对。

5.5 对比总结:什么时候用哪个?

布局类型 核心特点 适用场景 性能注意
LinearLayout 线性排列,支持权重 简单列表、表单、按钮组 避免超过2层嵌套
RelativeLayout 相对定位,灵活 旧项目维护、简单相对布局 子视图多时性能差
ConstraintLayout 约束驱动,扁平化 复杂界面、新项目首选 注意约束链不要过于复杂
FrameLayout 层叠显示,轻量 Fragment容器、占位、覆盖层 子视图不宜过多

最后说一句:没有最好的布局,只有最合适的。你想想看,一个简单的进度条用 FrameLayout 就够了,非要用 ConstraintLayout 反而画蛇添足。反过来,一个复杂的详情页用 LinearLayout 嵌套,那就是给自己挖坑。选布局的时候,多想想「这个界面以后会不会改?」、「子视图多不多?」、「需不需要适配不同屏幕?」——想清楚了再动手。

我的个人经验总结:

  • 新项目:全部用 ConstraintLayout,配合 Guidelines 做自适应
  • 简单列表项:LinearLayout 一层搞定,别嵌套
  • Fragment 容器:FrameLayout,轻量高效
  • 旧项目维护:如果遇到 RelativeLayout 性能问题,优先替换为 ConstraintLayout

公众号:蓝海资料掘金营,微信deep3321