向Activity中添加Fragment时,如何优化最终生成的布局?
嘿,这个场景太常见了——把Fragment嵌入Activity的FrameLayout容器后,咱们的布局层级会变成CoordinatorLayout → FrameLayout → LinearLayout,这里面藏着不少可以精简和优化的空间,下面是几个落地性很强的方案:
用
<merge>标签砍掉冗余的中间层级
如果你的Fragment根布局那个LinearLayout只是当个“容器壳”,没设置特殊属性(比如orientation搭配layout_weight、独特的内边距/外边距这类必须靠它实现的效果),那直接把Fragment的布局根换成<merge>标签就行!这样当Fragment被添加到Activity的FrameLayout里时,<merge>里面的内容会直接成为FrameLayout的子View,跳过原来的LinearLayout,把布局层级从三层精简到两层,少一层ViewGroup就少一次绘制计算的开销。修改后的fragment.xml示例:
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 原来LinearLayout里的所有子View直接放在这里就行 --> </merge>小提醒:用
<merge>的前提是Fragment的父容器(也就是Activity里的FrameLayout)的布局行为和你原来的根ViewGroup兼容——比如原来的LinearLayout只是垂直排列但没使用权重,那FrameLayout完全能hold住;要是原来的LinearLayout有复杂的权重布局,那还是得保留它,或者换用ConstraintLayout来重构。用
ConstraintLayout实现扁平布局
如果Fragment里的布局比较复杂,原来用LinearLayout可能要嵌套好几层ViewGroup,那果断把Fragment的根布局换成ConstraintLayout!它能以扁平的层级实现各种复杂的布局排列,彻底告别嵌套地狱。甚至你还可以把Activity里的FrameLayout也换成ConstraintLayout(只要给它加上app:layout_behavior="@string/appbar_scrolling_view_behavior"来配合AppBarLayout的滚动就行),整个布局的层级会更清爽,绘制性能也会提升不少。举个修改后的activity.xml例子:
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content"/> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/content_from_activity" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"/> </android.support.design.widget.CoordinatorLayout>清理重复的布局属性
检查下Activity的FrameLayout和Fragment的根ViewGroup有没有重复设置的属性——比如两者都设了match_parent的宽高、相同的内边距。这种情况下把重复的属性统一放在父容器(Activity的FrameLayout)里就行,Fragment的布局直接继承,减少不必要的属性解析工作。用
ViewStub实现延迟加载
如果Fragment里有部分内容不是一开始就要显示的(比如需要用户点击按钮才出现的模块),把这部分内容用<ViewStub>包裹起来!这样初始化布局时不会加载这部分View,直到你需要的时候调用stub.inflate()才会加载,能显著减少初始布局的绘制时间和内存占用。示例代码:
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 页面初始化就显示的内容 --> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="默认显示的内容"/> <!-- 延迟加载的内容,需要时再inflate --> <ViewStub android:id="@+id/delayed_stub" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout="@layout/layout_delayed"/> </merge>
这些优化核心都是围绕减少布局层级、避免过度绘制、按需加载这几个点,你可以根据自己的实际布局需求来选择合适的方案~
内容的提问来源于stack exchange,提问作者Cheok Yan Cheng

