Android中XML布局文件的使用非常频繁,在加载XML布局的时候,如果对XML文件其进行优化,将会提高加载的效率,为我们带来更好的体验。我在这里对一些常见的优化方法进行一下小结,以作为记录。
在Android加载XML布局文件,创建View或者ViewGroup的时候,XML文件的布局的深度和广度都会对这个过程造成影响,具体请参考
http://lishoubo.github.io/2013/08/12/android-measure-procedure/ android measure procedure ,这篇博客对该过程作了详细的解读。因此XML文件布局的深度和广度都会使创建View的过程更加的耗时,尤其是在布局文件多层嵌套的情况下,因此我们在进行布局的时候,应该尽量考虑使用扁平化的结构,减少布局的深度和复杂性。
常见情况下我们会使用LinearLayout和RelativeLayout以及FrameLayout,在Android 4.0以后,系统默认使用RelativeLayout作为布局文件的顶层节点,因为其能更准确的获知其中View的位置和大小,相对而言,LinearLayout则需要多次进行测量布局操作,才能获取到正确的信息。RelativeLayout可以更加方便地实现一些LinearLayout需要进行嵌套才能实现的一些布局,这样对于实现布局的扁平化有更好的效果。
例如要实现下面的布局效果
使用LinearLayout的代码如下:
使用RelativeLayout的代码如下:
通过比较可以看出,使用RelativeLayout的情况下,可以减少一个LinearLayout的使用,使得所有的ImageView控件都处于同一个层级,降低了布局结构的深度,在使用该布局创建View的时候,可以提高效率。这种情况是一种很常见的情形,也非常的简单,我们随手就可以做到。坚持这种做法,并在更复杂的布局中使用这一原则,一定会收到更好的效果。
同样,我们可以减少布局中不必要的控件的使用,以达到精简布局的效果。例如通过为TextView设置图片,来替换ImageView与TextView的组合,实现同样的效果,而这样只使用了一个控件。还有其他一些小技巧,例如使用HTML标签优化TextView显示,可以参考 http://blog.csdn.net/ljtyzhr/article/details/42402043
2.include标签的使用
在应用的多个界面中,经常会有几个界面存在相同或者类似的部分,例如标题栏,导航栏之类的。这些重复的界面如果一一写下来,既浪费时间精力,又不方便统一管理,例如需要改动的时候,在这种情况下,可以把这个重复的界面布局独立出来,然后通过include标签,将这个布局导入需要引用的地方,这样就可以只维护这个独立出来的布局文件即可。
假设这是一个可供复用的独立布局文件,文件名为titlebar.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width=”match_parent”
android:layout_height="wrap_content"
android:background="@color/titlebar_bg">
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/gafricalogo" />
FrameLayout>
然后你就可以在需要引入这部分布局的文件中,通过include标签,导入这部分布局。
<include layout="@layout/titlebar"/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:background="@color/app_bg"
android:gravity="center_horizontal">
<include layout="@layout/titlebar"/>
<TextView android:layout_width=”match_parent”
android:layout_height="wrap_content"
android:text="@string/hello"
android:padding="10dp" />
...
LinearLayout>
然后titlebar.xml中的布局就替换了include标签位置的部分。
在使用include标签的时候需要注意:
可以复写所有layout属性(任何android:layout_*属性),但是必须同时复写layout_width和 layout_height属性,这些复写的layout属性才会生效。例如:
<include android:id="@+id/news_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
layout="@layout/title"/>
你也可以在include标签中给这个布局定义一个id,这样将会覆盖其独立布局文件中的id值。在声明独立布局中的子View的时候,需要先
FrameLayout titleView = findViewById(R.id.new_title);
然后再在titleView中使用 titleView.findViewById() 方法,直接使用findViewById将无法找到其中的子View。
merge标签可以用来代替布局文件中的根节点,例如LinearLayout,RelativeLayout,FrameLayout等元素。配合include标签,可以减少布局的嵌套。例如
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/add"/>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/delete"/>
merge>
使用include标签将以上布局复用的时候,将会直接把两个Button放在include标签的位置,而不会再使用其他容器来包装这两个Button,这样就会减少不必要的嵌套,有利于布局的扁平化。
4.ViewStub使用
在实际应用中,可能存在一部分界面是处于隐藏状态的,在一定的条件下,才会展示出来。我们一般会将这部分节目使用setVisibility(View.GONE)方法将其隐藏起来,但是我们更推荐使用ViewStub来达到相同的目的。因为ViewStub是一个处于不可见状态的View,初始状态时不会被加载出来,不会浪费资源,而处于View.GONE状态的View也是需要被加载的。ViewStub的使用方法类似include标签,需要在其中声明需要加载的布局文件
my_comment_layout布局如下: 在需要加载这部分布局的时候,可以通过两种方式实现加载:通过setVisibility(View.VISIBLE)的方式,这时候可以通过判断其可见性来得知其是否已经加载
// 方式1,获取ViewStub, ViewStub listStub = (ViewStub) findViewById(R.id.stub_import); // 加载评论列表布局 listStub.setVisibility(View.VISIBLE); // 获取到评论ListView,注意这里是通过ViewStub的inflatedId来获取 ListView commLv = findViewById(R.id.stub_comm_lv); if ( listStub.getVisibility() == View.VISIBLE ) { // 已经加载, 否则还没有加载 } }通过inflate方法来实现加载。注意此时无法通过判断可见性来判断是否已经加载了,可以通过判断其中的元素是否存在来判断加载情况。
// 把commLv2设置为类的成员变量 ListView commLv2 = null; // public void onCreate(Bundle b){ // main.xml中包含上面的ViewStub setContentView(R.layout.main); // 方式二 ViewStub listStub2 = (ViewStub) findViewById(R.id.stub_import) ; // 成员变量commLv2为空则代表未加载 if ( commLv2 == null ) { // 加载评论列表布局, 并且获取评论ListView,inflate函数直接返回ListView对象 commLv2 = (ListView)listStub2.inflate(); } else { // ViewStub已经加载 } }需要注意的是,如果ViewStub设置了inflatedId,那么将需要通过该id来查找布局的根元素。详细讲解请阅读 http://www.codeceo.com/article/android-viewstub-include-merge.html
5.hierarchy viewer工具的使用
6.其他优化
参考:
http://www.codeceo.com/article/android-viewstub-include-merge.html
未完待续