Android之布局优化
Android 之布局优化,关于 include、merge、ViewStub
使用 Android Studio 时,可以直接在布局文件对应控件:
右键 -> Refactor -> Extract -> Style 抽取样式
右键 -> Refactor -> Extract -> Layout 抽取布局 include 标签
include
include 标签常用于将布局中的公共部分提取出来供其他 Layout 共用,以实现布局模块化,也是平常我们设计布局时用的最多的。
一个 xml 布局文件中若有多个 include 标签需要设置 ID,才能找到相应子 View 的控件,否则只能找到第一个 include 的 Layout 布局以及其控件;使用 layout_xx 属性会覆盖被 include 的 xml 文件根节点对应的属性,设置 ID 也会对应覆盖;如果要在 include 标签下使用 RelativeLyout,如 layout_margin 等其他属性,记得要同时设置 layout_width 和 layout_height,不然其他属性会没反应。
merge
merge 标签主要用于辅助 include 标签,在使用 include 后可能导致布局嵌套过多,多余的 layout节点或导致解析变慢(可通过 hierachy viewer 工具查看布局嵌套情况)。merge 用于消除视图层次结构中的冗余视图,例如根布局是 LinearLayout,又 include 一个 LinearLayout 布局就没意义了,反而会减慢 UI 的加载速度。
merge 标签常用场景:
- 根布局是 FrameLayout 且不需要设置 background 或 padding 等属性,可以用 merge 代替。Activity 的 ContentView 父元素就是 FrameLayout。
- merge 作为顶节点被 include 到其他布局时,会被自动忽略而其子节点全部合并到对应布局中。
- 自定义 View 如果继承 LinearLayout(ViewGroup),建议让自定义 View 的布局文件根布局设置成 merge,这样能少一层节点。
merge 不是 View,所以通过LayoutInflate.inflate()
时,第二个参数比逊指定一个父容器,且第三个参数比须为 true,也就是比须为 merge 下的视图指定一个节点;merge 标签比须使用在根布局;ViewStub 标签中的 Layout 布局不能使用 merge 标签。
ViewStub
ViewStub 标签最大的优点就是当你需要时才会加载,使用它不会影响 UI 初始化时的性能。各种不常用的布局进度条、显示错误信息等可以用 ViewStub 标签,以减少内存使用量,加快渲染速度。ViewStub 是一个不可见,实际上是把宽高设置为0的 View,效果有点类似普通的 view.setVisible(),但是性能体验提高不少。
例子:
1
2
3
4
5
6
7
8
9<ViewStub
android:id="@+id/stub_import"
<!-- android:inflateId: 重写 ViewStub 的父布局控件的 Id -->
android:inflateId="@+id/panel_import"
<!-- android:layout: 设置 ViewStub 被 inflate 的布局 -->
android:layout="@layout/progress_overlay"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" />
1 | ((ViewStub)findViewById(R.id.stub_import)).setVisibily(View.VISIBLE); |
判断 ViewStub (做单例)是否已经加载过:
- 如果通过
setVisibily()
来加载,判断可见性 - 如果通过
inflate()
来加载,判断 ViewStub 的 ID 是否为 null
不支持 merge 标签;ViewStub 的inflate()
只能被调用一次,第二次调用会抛出异常;setVisibily()
可以被调用多次,但不建议这样做(ViewStub 调用过后,可能被 GC 掉,再调用setVisibily()
会报异常);为 ViewStub 设置的 android:layout_xx 属性会替换待加载布局文件根节点的对应属性
具体优化方案:
删除布局中无用的控件和层级;
选择耗费性能较少的布局:LinearLayout 优于 RelativeLayout(RelativeLayout 在绘制时需要对子 View 分别进行竖直和水平方向的两次测量,而 LinearLayout 则是根据我们设置的方向分别调用不同的测量方法。注意一点如果 LinearLayout 中子 View 使用了 layout_weight 属性时同样需要进行两次测量以确定最终大小)。LinearLayout 和 FrameLayout 都是一种性能消耗低的布局
- 性能消耗低的布局 = 功能简单 = FrameLayout、LinearLayout
- 性能消耗高的布局 = 功能复杂 = RelativeLayout (ConstrainLayout)
- 嵌套所消耗的性能 > 单个布局本身消耗的性能
提高布局的复用性(使用
尽可能少用布局属性 wrap_content,其会增加布局测量时的计算成本;
减少控件的使用(善用控件的属性):TextView 文字加图片、LinearLayout 分割线、TextView 的行间距和占位符的使用
布局调优工具:Lint、Hierarchy Viewer、开发者选项(调试 GPU 过度绘制)
参考引用
Android布局优化之ViewStub、include、merge使用与源码分析
Android 优化之路(一)布局优化