本节摘要:FrameLayout 把子控件统统摞在左上角,靠 layout_gravity 挪位,是叠放与占位场景的最简容器。本节讲它的两大本职——背景层叠与容器替代,再收束全章:margin 与 padding、gravity 与 layout_gravity 这两对通用属性,是所有容器共享的词汇。
FrameLayout 的规则一句话讲完:子控件默认全部堆在左上角,后声明的盖在先声明的上面。听着简陋,但"叠放"这件事非它不可。猜数字 App 的两个场景都靠它:其一是表情图上叠状态角标——胜利时左上角浮一个金色星星:
<FrameLayout android:layout_width="96dp" android:layout_height="96dp"> <ImageView android:id="@+id/iv_face" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" android:src="@drawable/ic_face_smile" /> <TextView android:id="@+id/tv_badge" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|end" android:text="WIN" android:textSize="10sp" /> </FrameLayout>
ImageView 铺满容器做底层,角标用 layout_gravity="top|end" 钉到右上角——gravity 值可以用竖线组合,一次钉住两根轴。其二是 Fragment 容器:Activity 布局里预留一个空的 FrameLayout 当"舞台",第 7 章的 DialogFragment、乃至更复杂的单 Activity 多页结构,都是往这个舞台里换演员。给它一个 id 就够了,它自己永远空着。
代码动态换演员也是三行的事:
supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, ResultFragment()) .commit()
⚠️ 注意:FrameLayout 不负责给子控件排队,用它装一列表单控件只会得到一摞叠罗汉。判断标准很简单——需要"排列"去找前三节,需要"叠放或占位"才来 FrameLayout。
四套容器讲完,该把全章通用的词汇收进一个抽屉。第一对是 margin 与 padding,猜数字的按钮正好同时用到两者:
<Button android:id="@+id/btn_guess" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="16dp" android:padding="12dp" android:text="提交" />
margin 是控件边界之外的留白,向父容器申请;padding 是控件边界之内的留白,向自己的内容申请。按钮文字离边框远一点是 padding 的事,按钮离屏幕边缘远一点是 margin 的事。判断口诀:空白在框外是 margin,在框内是 padding。方向化版本 marginStart/end 比 left/right 更值得写——阿拉伯语等 RTL 布局下 start/end 会自动镜像,left/right 不会,这是 2.3 节可访问性话题在布局层的延伸。
第二对易混属性本章出现过两次(1.1 的文字对齐、4.1 的标题居中),这里正式合并讲透:
<LinearLayout android:orientation="vertical" ...> <!-- layout_gravity:这个 TextView 自己在父容器里靠右 --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" android:gravity="center" android:text="剩余次数 3" /> </LinearLayout>
前缀有无决定作用域:带 layout_ 前缀的是向父容器说话——"把我放在哪";不带的是向自己内部说话——"我的内容摆哪"。还有一个隐蔽陷阱:layout_gravity 的效果取决于父容器是谁——LinearLayout 竖排时 layout_gravity 管水平挪位(竖直方向被排队规则占用),FrameLayout 里则两轴都自由。同一行代码换个容器效果就变,这不是 bug,是"属性其实是发给父容器的请求"这一本质的体现。
