本节摘要:RelativeLayout 用"相对于谁"的定位属性把控件钉在彼此或父容器旁边,适合快速实现"图在文字左侧、按钮贴屏幕底部"这类贴邻关系。本节重构猜数字 App 的底部操作区,讲透 id 引用网络、alignBaseline 对齐与循环依赖报错。
LinearLayout 只会排队,说不出"A 在 B 左边"这种关系。RelativeLayout 的词汇表全是由"相对于谁"构成的:猜数字 App 的结果区要做成"表情图在反馈文字左侧、按文字基线对齐",正好用它:
<RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/iv_face" android:layout_width="48dp" android:layout_height="48dp" android:layout_alignParentLeft="true" android:src="@drawable/ic_face_smile" /> <TextView android:id="@+id/tv_result" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@id/iv_face" android:layout_alignBaseline="@id/iv_face" android:layout_marginLeft="8dp" android:text="太大了,往小猜" /> </RelativeLayout>
三句定位语言各自的作用:layout_alignParentLeft 把表情图钉在父容器左缘;layout_toRightOf="@id/iv_face" 让文字站到图的右侧——这行代码建立了一个 id 引用,被引用者必须已经存在;layout_alignBaseline 让文字的基线与图的基线对齐,混合高度控件并排时基线对齐比 top 对齐更耐看。
RelativeLayout 的属性可以按"参照物"分成三组记:
<!-- 相对父容器 --> android:layout_alignParentTop="true" android:layout_centerInParent="true" <!-- 相对其他控件 --> android:layout_below="@id/tv_result" android:layout_toLeftOf="@id/btn_clear" android:layout_alignTop="@id/iv_face" <!-- 玩转边界 --> android:layout_alignEnd="@id/btn_guess" android:layout_marginStart="12dp"
一个实战例子:把"提交"与"清空"按钮贴到屏幕底部并排。给外层 RelativeLayout 里的按钮行容器写 android:layout_alignParentBottom="true" 与 android:layout_centerHorizontal="true",再把主内容区声明 `android:layout_above="@id/oper_row"``——底部固定、上方让位,一屏小界面就有了"页脚"。这类"沉底"需求用 LinearLayout 得靠权重绕弯,RelativeLayout 两行搞定,这是它在旧代码里大量存在的原因。

循环依赖:A 写了 layout_below="@id/b",B 又写 layout_below="@id/a"——两个控件互相等待对方先定位,运行时直接抛出 IllegalStateException。报错信息里带两个 id 名字,顺着删掉一条引用即可。
默认堆叠:什么定位属性都不写的子控件全部挤在 RelativeLayout 左上角,互相覆盖。看到"控件神秘消失",先查它是不是被后来的兄弟盖住了——Android Studio 的布局检查器里能直观看到覆盖关系。
id 声明顺序:@id/ 引用要求 id 已定义,@+id/ 则是"没有就现场创建"。XML 里先出现的控件先拿到 id,所以习惯上把被引用者写在前面;实在要后向引用,可以第一次出现时写 @+id/xxx,之后引用处用 @id/xxx。
代码侧几乎不用碰布局本身,但动态改相对位置偶尔有需求,思路是改 LayoutParams 而不是反复重排 XML:
val params = binding.tvResult.layoutParams as RelativeLayout.LayoutParams params.addRule(RelativeLayout.BELOW, R.id.iv_face) binding.tvResult.layoutParams = params
追问:RelativeLayout 被淘汰了吗? 没有,它仍是 framework 自带、零依赖开销的实用容器。谷歌不再把它列为新项目首选,是因为 ConstraintLayout 表达力全面覆盖且性能更优;但读旧代码、改历史项目是每个开发者的日常,读懂它的能力不会贬值。真正该淘汰的是"用两层嵌套 RelativeLayout 硬凑位置"的写法——那不是工具的错,是使用的错。