转载:http://blog.csdn.net/u013718120/article/details/51351738
今天,我们继续Android性能优化系列 - 布局优化。在Android中,UI布局作为展示性的标志,显示的速度直接体现了一个App对于客户直观的影响。一个好的App,在布局和UI上肯定有比较好的性能优化,所以布局优化成为了Android性能优化的有一个重点。
Android中关于布局优化,系统为我们提供了几个抽象的标签:
(1)include
(2)viewstub
(3)merge
下面,我们就来一一介绍三个标签的作用和使用方式。
1> include
顾名思义,include标签可以让我们复用包含一个layout布局。例如,我们App在大部分情景下都是这样的设计:topbar -> content -> footbar。
那么,很多界面都是这样的布局,我们就可以抽取出topbar和footbar作为公共布局,使用include就可以复用该布局,下面看代码:
这是一个头布局:layout_topbar.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="46dp"
android:id="@+id/rl_root_title_base"
>
<TextView
android:id="@+id/tv_left_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:gravity="center_vertical"
android:textColor="#80C469"
/>
<TextView
android:id="@+id/tv_topic_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="@color/cl_font_46"
android:textSize="18dp"
android:maxLength="10"
android:ellipsize="end"
android:singleLine="true"
/>
<TextView
android:id="@+id/tv_right_first_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:gravity="center"
android:minHeight="30dp"
android:minWidth="30dp"
android:layout_marginRight="10dp"
android:textSize="16dp"
android:textColor="@color/cl_80c469"
/>
<TextView
android:id="@+id/tv_right_second_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/tv_right_first_title"
android:layout_centerVertical="true"
android:gravity="center"
android:minHeight="30dp"
android:minWidth="30dp"
android:layout_marginRight="10dp"
android:textColor="#80C469"
/>
</RelativeLayout>
<View
android:id="@+id/line_root_base"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="@color/cl_font_ea" />
</LinearLayout>
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<include layout="@layout/layout_top" />
</LinearLayout>
上面代码中我们使用include的属性layout来重用topbar布局。include还可以指定layout_width和layout_height以及id。如果指定了id属性,则被包含的根布局id将会失效,此id将会被覆盖。
2> viewstub
viewstub标签和include标签很相似,都是使用layout属性来加载一个布局。不同之处是include标签用来重用布局,该布局会在App启动加载时被加载进内存。而viewstub标签所引用的布局是不会被加载出来的,只有当你在某个时间需要使用时才会被加载出来,从而在解析layout时节省了cpu加载时间和内存的占用。
使用也非常简单,看下面代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<ViewStub
android:id="@+id/vstub_network_error"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/layout_network_error"
/>
</LinearLayout>
在布局代码中,我们使用ViewStub的layout来加载一个布局。
private void showEmptyView() {
if(ValidateUtils.isNullObj(viewNetWorkError)){
ViewStub mViewStub = (ViewStub) findViewById(R.id.vstub_network_error);
viewNetWorkError = mViewStub.inflate();
} else {
viewNetWorkError.setVisibility(View.VISIBLE);
}
}
private void showCourseContent(Course course) {
if(ValidateUtils.isNotNullObj(viewNetWorkError)) {
viewNetWorkError.setVisibility(View.GONE);
}
}
我们在代码中通过findViewById来获取ViewStub,然后调用其inflate方法就可以获取到layout的view。
ViewStub的使用场景很广泛:例如显示加载失败的界面,进度布局显示,信息出错提示等等。
3> merge
merge标签用来取消layout的层级的显示。大家都知道,Android顶级布局DecorView的根布局是让FrameLayout。此时在我们的布局中如果我们使用FrameLayout,就会造成布局节点的层级叠加重复绘制。Android为我们提供了一个叫做hierarchy viewer来查看布局节点信息。该工具在sdk/tools目录下。
要注意的一点是,merge标签必须作为布局的根标签,即不能嵌套在其他布局中。
使用很简单,就不贴代码了。
介绍完了以上3个抽象标签的使用方式,想必大家都知道该如何去应用在自己的布局中来优化布局,下面还有几点同时也需要大家去关注一下:
(1)inflate:大家都知道,inflate用来在代码中加载一个现有的布局。例如我们可以使用LayoutInflater.from().inflate()来发加载,同时也可以在Activity中使用getLayoutInflater().inflate()来加载一个布局。但是每次使用inflate去加载布局是有时间和内存消耗的。所以我们要利用复用View来减少不必要的重复inflate。例如上面我们在介绍ViewStub的时候会去inflate。此时我们就可以将该View当做全局变量。下次只需要判断下是否为null,就可以去重复使用,而不必每次都inflate加载了。
(2)ListView Adapter的getView方法:在该方法中我们都会去inflate一个布局,然后设置一些数据,最后return该View。ListView在每一个Item被渲染加载时都回去重复调用getView。并且ListView的convertView是可以重复利用的,所以我们可以利用ViewHolder的原理来复用convertView,而不必每次都inflate。
(3)使用SurfaceView和TextureView:SurfaceView和TextureView不会占用UI Thread来加载渲染视图,他们都会开启一个线程来异步进行加载。所以可以使用他们来优化加载速度及显示效果。
(4)尽量为所有分辨率创建资源:减少不必要的硬件缩放,这样会降低UI的绘制速度和占用大量的内存消耗。
使用复杂的图片:在LineraLayout布局中包含一个ImageView和一个TextView,可以使用一个复杂的drawable代替,性能会更好。
合并根框架:假如一个FrameLayout作为一个布局的根视图,不提供背景或者填充,它可以被一个带有标志的布局代替。
无用的树叶:对于一个扁平结构中一个布局没有孩子,没有背景,可以被删掉。
无用的父类:一个布局不是ScrollView或者不是一个根布局,也没有背景,只有一个孩子节点,可以被删掉,孩子节点直接放入到这个扁平的父类里面。
深度布局:布局若有太多内嵌,则性能很差。考虑使用RelativeLayout 以及GridLayout等扁平化布局代替。默认布局最大深度是10.