一、页面布局对App性能的影响和原因
布局性能主要影响 :App的页面显示速度。
布局影响性能的实质:页面的测量 & 绘制时间,一个页面通过递归完成测量 & 绘制过程。
二、优化思路
合理的布局性能、布局层级、布局复用性 和 按需加载(测量&绘制时间)。
在布局优化中,Android的官方提到了这三种标签<include />
、<merge />
、<ViewStub />
,下面也是简单说一下它们的优势,以及怎么使用。
三、具体方案
(一) 提高布局的复用性<include/>
1、作用:提取布局间的公共部分,通过提高布局的复用性从而减少测量 & 绘制时间。
2、使用:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:background="@color/white"
android:orientation="vertical" >
<include layout="@layout/titlebar"/>
<TextView
android:layout_width=”match_parent”
android:layout_height="wrap_content"
android:text="@string/Hello World"
android:padding="10dp" />
</LinearLayout>
3、特别注意:
(1)<include/>
标签可以使用单独的layout属性,这个也是必须使用的。
(2)<include/>
标签若指定了id属性,而layout也定义了id,则layout的id会被覆盖。
(二) 减少视图层级<merge/>
1、作用:减少布局层级,配合<include/>
标签使用,可优化加载布局文件时的资源消耗。
2、使用:
<merge/>
标签在UI的结构优化中起着非常重要的作用,它可以删减多余的层级,优化UI。<merge/>
多用于替换FrameLayout或者当一个布局包含另一个时,<merge/>
标签消除视图层次结构中多余的视图组。
例如:你的主布局文件是相对布局,引入了一个相对布局的<include/>
,这时<include/>
布局使用的RelativeLayout就没意义了,反而减慢UI渲染。这时可以使用<merge/>
标签优化。
//布局A:layout_a.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_10"/>
<TextView
android:id="@+id/textview"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_10"/>
</RelativeLayout>
//布局B:layout_b.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/Button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/dp_10" />
<include layout="@layout/layout_a.xml" />
</RelativeLayout>
在上述例子,在布局B中通过<include/>
标签引用布局A,
此时B布局层级为 = RelativeLayout ->> Button
—>> RelativeLayout
->> Button
->> TextView
现在使用<merge/>
优化,将被引用布局A根标签的RelativeLayout改为<merge/>
。那么在引用布局A时,布局A中的<merge/>
标签内容根节点RelativeLayout会被去掉,在<include/>
标签里存放的是布局A中的<merge/>
标签为根节点的子节点即<include/>
里存放的是:<Button/>
和<TextView/>
。
此时B布局层级为 = RelativeLayout ->> Button
->> Button
->> TextView
即已去掉之前无意义且多余的<RelativeLayout/>
,
//布局A:layout_a.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_10"/>
<TextView
android:id="@+id/textview"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_10"/>
</merge>
(三) 按需加载<ViewStub/>
1、作用:按需加载 外部引入的布局,注:属轻量级View、不占用显示 & 位置。
<ViewStub/>
标签最大的优点是当你需要时才会加载,使用它并不会影响UI初始化时的性能。各种不常用的布局想进度条、显示错误消息等可以使用<ViewStub/>
标签,以减少内存使用量,加快渲染速度。<ViewStub/>
是一个不可见的,大小为0的View。
2、使用:
// 步骤1、先设置好预显示的布局
// 步骤2、在其他布局通过<ViewStub>标签引入外部布局(类似<include>)
注:此时该布局还未被加载显示。
// 步骤3、只有当ViewStub被设置为可见ViewStub.setVisibility(View.VISIBLE)或者
调用ViewStub.inflate()时,ViewStub所指向的布局文件才会被inflate、
实例化,最终 显示<ViewStub>指向的布局。
/**
* 实例说明:在布局A中引入布局B,只有在特定时刻C中才显示
*/
// 步骤1:先设置好预显示的布局B = layout_b.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_10"/>
<TextView
android:id="@+id/textview"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_10"/>
</RelativeLayout>
// 步骤2:在布局A通过<ViewStub>标签引入布局B(类似<include>);注:此时该布局还未被加载显示
// 布局A:layout_a.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/Button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/dp_10" />
<ViewStub
android:id="@+id/Blayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/layout_b" />
</RelativeLayout>
// 步骤3:
只有当ViewStub被设置为可见 / 调用了ViewStub.inflate()时,ViewStub所指向的布局文件
才会被inflate 、实例化,最终 显示<ViewStub>指向的布局
ViewStub stub = (ViewStub) findViewById(R.id.Blayout);
stub.setVisibility(View.VISIBLE);
//or
stub.inflate();
3、特别注意:
(1) ViewStub中的layout布局不能使用merge标签,否则会报错。
(2)ViewStub的inflate只能执行一次,显示了之后,就不能再使用ViewStub控制它了。
(3)与View.setVisible(View.Gone)的区别:
-
共同点:初始时不会显示
-
不同点:ViewStub标签只会在显示时,才会渲染整个布局。View.GONE在初始化布局树的时候就已经添加在布局树上了。相比之下ViewStub标签节省布局文件的解析时间和内存的占用,从而具有更高的效率。
(四) 选择耗费性能较少的布局
1、性能耗费低的布局 = 功能简单 = LinearLayout、FrameLayout
2、性能耗费高的布局 = 功能复杂 = RelativeLayout,即布局过程需消耗更多性能(CPU资源 & 时间)
3、嵌套多个布局所耗费的性能 > 单个布局本身耗费的性能,即完成需求时:宁选择单个耗费性能高的布局,也不采用嵌套多个耗费性能低的布局。
4、通过合理选择布局类型,从而减少嵌套。即:完成复杂的UI效果时,尽可能选择1个功能复杂的布局(如RelativeLayout)完成,而不要选择多个功能简单的布局(如LinerLayout)通过嵌套完成。