Android 布局优化

移动开发 Android
在开发过程中我们经常说性能优化,但性能优化是一个比较宽泛的概念。在Android开发中性能优化可能包括:Java代码优化, 算法优化, SQLite优化, 布局优化等。那么这篇博客就来总结并分享下Android开发中的布局优化。

布局原则

在Android UI布局过程中,通过遵守一些惯用、有效的布局原则,我们可以制作出高效且复用性高的UI,概括来说包括如下几点:

尽量多使用RelativeLayout 和LinearLayout, 不要使用绝对布局AbsoluteLayout,在布局层次一样的情况下, 建议使用LinearLayout代替RelativeLayout, 因为LinearLayout性能要稍高一点,但往往RelativeLayout可以简单实现LinearLayout嵌套才能实现的布局。

将可复用的组件抽取出来并通过include标签使用;

使用ViewStub标签来加载一些不常用的布局;

使用merge标签减少布局的嵌套层次;

RelativeLayout VS LinearLayout

***条原则说了布局层次一样的情况下LinearLayout比RelativeLayout要好, 但往往RelativeLayout可以简单实现LinearLayout嵌套才能实现的布局。假如需要实现如下布局:

用LinearLayout来实现xml代码如下:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     android:layout_width="fill_parent" 
  3.     android:layout_height="?android:attr/listPreferredItemHeight" 
  4.     android:padding="6dip"
  5.      
  6.     <ImageView 
  7.         android:id="@+id/icon" 
  8.         android:layout_width="wrap_content" 
  9.         android:layout_height="fill_parent" 
  10.         android:layout_marginRight="6dip" 
  11.         android:src="@drawable/icon" /> 
  12.  
  13.     <LinearLayout 
  14.         android:orientation="vertical" 
  15.         android:layout_width="0dip" 
  16.         android:layout_weight="1" 
  17.         android:layout_height="fill_parent"
  18.  
  19.         <TextView 
  20.             android:layout_width="fill_parent" 
  21.             android:layout_height="0dip" 
  22.             android:layout_weight="1" 
  23.             android:gravity="center_vertical" 
  24.             android:text="My Application" /> 
  25.              
  26.         <TextView   
  27.             android:layout_width="fill_parent" 
  28.             android:layout_height="0dip" 
  29.             android:layout_weight="1"  
  30.             android:singleLine="true" 
  31.             android:ellipsize="marquee" 
  32.             android:text="Simple application that shows how to use RelativeLayout" /> 
  33.              
  34.     </LinearLayout></LinearLayout> 

而用RelativeLayout实现代码如下:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     android:layout_width="fill_parent" 
  3.     android:layout_height="?android:attr/listPreferredItemHeight" 
  4.     android:padding="6dip"
  5.      
  6.     <ImageView 
  7.         android:id="@+id/icon" 
  8.         android:layout_width="wrap_content" 
  9.         android:layout_height="fill_parent" 
  10.         android:layout_alignParentTop="true" 
  11.         android:layout_alignParentBottom="true" 
  12.         android:layout_marginRight="6dip" 
  13.         android:src="@drawable/icon" /> 
  14.  
  15.     <TextView   
  16.         android:id="@+id/secondLine" 
  17.         android:layout_width="fill_parent" 
  18.         android:layout_height="26dip"  
  19.         android:layout_toRightOf="@id/icon" 
  20.         android:layout_alignParentBottom="true" 
  21.         android:layout_alignParentRight="true" 
  22.         android:singleLine="true" 
  23.         android:ellipsize="marquee" 
  24.         android:text="Simple application that shows how to use RelativeLayout" /> 
  25.  
  26.     <TextView 
  27.         android:layout_width="fill_parent" 
  28.         android:layout_height="wrap_content" 
  29.         android:layout_toRightOf="@id/icon" 
  30.         android:layout_alignParentRight="true" 
  31.         android:layout_alignParentTop="true" 
  32.         android:layout_above="@id/secondLine" 
  33.         android:layout_alignWithParentIfMissing="true" 
  34.         android:gravity="center_vertical" 
  35.         android:text="My Application" /></RelativeLayout> 

可以看到用RelativeLayout实现,布局层次明显少了,所以大多数时候优先推荐使用RelativeLayout。

 

查看布局层次

如何查看布局层次呢?有两种办法:一是通过手机的开 发者选项,4.0及以上Android版本可通过设置->开发者选项->显示布局边界打开页面布局显示,看看是否有不必要的节点和嵌套。第二 种就是利用SDK自带的UI性能检测工具HierarchyViewer。 进入sdk目录下的tools文件夹下,找到HierarchyViewer并运行(此时保持你的模拟器或真机正在运行需要进行分析的App),双击我们 正在显示的这个App所代表的进程。接下来便会进入hierarchyviewer的界面,我们可以在这里很清晰看到正在运行的UI的布局层次结构以及它 们之间的关系。大概的显示如下图:

通过布局图我们可以看到根节点DecorView下 包含一个LinearLayout, 这个LinearLayout就是包含Activity布局和状态栏的整个屏幕显示的布局父节点,这个LinearLayout有两个子节点, 一个是FrameLayout, FrameLayout就是Activity布局中默认的父布局节点, 这个节点下面就包含了我们自己写的xml布局, 还有一个子节点就是ViewStub,关于这个节点我们在后面会详细介绍。

 

< include />的使用

在实际开发中,我们经常会遇到一些共用的UI组件, 比如带返回按钮的导航栏,如果为每一个xml文件都设置这部分布局,一是重复的工作量大,二是如果有变更,那么每一个xml文件都得修改。还 好,Android为我们提供了include标签,顾名思义,通过它,我们可以将这些共用的组件抽取出来单独放到一个xml文件中,然后使用 include标签导入共用布局,这样,前面提到的两个问题都解决了。下面以在一个布局main.xml中用include引入另一个布局 header.xml为例。

header.xml文件

  1. <?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     android:layout_width="match_parent" 
  3.     android:layout_height="match_parent" > 
  4.  
  5.     <Button 
  6.         android:id="@+id/button" 
  7.         android:layout_width="match_parent" 
  8.         android:layout_height="@dimen/dp_40" 
  9.         android:layout_above="@+id/text"/> 
  10.  
  11.     <TextView 
  12.         android:id="@+id/text" 
  13.         android:layout_width="match_parent" 
  14.         android:layout_height="@dimen/dp_40" 
  15.         android:layout_alignParentBottom="true" 
  16.         android:text="@string/app_name" /></RelativeLayout> 

然后我们在需要引入footer的布局xml中通过include导入这个共用布局。

main.xml文件

  1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     android:layout_width="match_parent" 
  3.     android:layout_height="match_parent"
  4.  
  5.     <TextView 
  6.         android:layout_width="match_parent" 
  7.         android:layout_height="wrap_content" 
  8.         android:text="hello world" /> 
  9.  
  10.     <RelativeLayout  
  11.         android:layout_width="match_parent" 
  12.         android:layout_height="match_parent" 
  13.         android:layout_gravity="center" > 
  14.  
  15.         <include layout="@layout/header" /> 
  16.  
  17.     </RelativeLayout></FrameLayout> 

通过这种方式,我们既能提高UI的制作和复用效率,也能保证制作的UI布局更加规整和易维护。

 

< merge />的使用

merge标签的作用是合并UI布局,使用该标签能降低UI布局的嵌套层次。merge标签可用于两种典型情况:

  • 布局根结点是FrameLayout且不需要设置background或padding等属性,可以用merge代替,因为Activity内容布局的parent view就是个FrameLayout,所以可以用merge消除只剩一个,这一点可以从上图中看到。

  • 某布局作为子布局被其他布局include时,使用merge当作该布局的顶节点,这样在被引入时顶结点会自动被忽略,而将其子节点全部合并到主布局中。

以***种情况为例,main.xml布局就可以优化如下:

  1. merge xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     android:layout_width="match_parent" 
  3.     android:layout_height="match_parent"
  4.  
  5.     <FrameLayout  
  6.         android:layout_width="match_parent" 
  7.         android:layout_height="match_parent"
  8.  
  9.         <TextView 
  10.             android:layout_width="match_parent" 
  11.             android:layout_height="wrap_content" 
  12.             android:text="hello world" /> 
  13.  
  14.         <RelativeLayout  
  15.             android:layout_width="match_parent" 
  16.             android:layout_height="match_parent" 
  17.             android:layout_gravity="center" > 
  18.  
  19.             <include layout="@layout/header" /> 
  20.  
  21.         </RelativeLayout> 
  22.     </FrameLayout></merge> 

以第二种情况为例,header.xml布局可以优化如下:

  1. <?xml version="1.0" encoding="utf-8"?><merge xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     android:layout_width="match_parent" 
  3.     android:layout_height="match_parent" > 
  4.   
  5.     <Button 
  6.         android:id="@+id/button" 
  7.         android:layout_width="match_parent" 
  8.         android:layout_height="@dimen/dp_40" 
  9.         android:layout_above="@+id/text"/> 
  10.   
  11.     <TextView 
  12.         android:id="@+id/text" 
  13.         android:layout_width="match_parent" 
  14.         android:layout_height="@dimen/dp_40" 
  15.         android:layout_alignParentBottom="true" 
  16.         android:text="@string/app_name" /> 
  17.  </merge> 

这样就不会有多余的FrameLayout和RelativeLayout节点了。

 

ViewStub标签

viewstub标签同include标签一样可以 用来引入一个外部布局,不同的是,viewstub引入的布局默认不会扩张,即既不会占用显示也不会占用位置,从而在解析layout时节省cpu和内 存。 viewstub常用来引入那些默认不会显示,只在特殊情况下显示的布局,如进度布局、网络失败显示的刷新布局、信息出错出现的提示布局等。

我们新建一个xml文件用来显示一个网络错误时提示信息error.xml:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     xmlns:tools="http://schemas.android.com/tools" 
  3.     android:layout_width="wrap_content" 
  4.     android:layout_height="wrap_content" > 
  5.  
  6.    <TextView 
  7.         android:layout_width="wrap_content" 
  8.         android:layout_height="wrap_content" 
  9.         android:layout_centerInParent="true" 
  10.         android:background="@android:color/white" 
  11.         android:padding="10dip" 
  12.         android:text="Message" 
  13.         android:textColor="@android:color/black" /></RelativeLayout 

然后在main.xml里面加入ViewStub的标签引入上面的布局:

  1. <merge xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     xmlns:tools="http://schemas.android.com/tools" 
  3.     android:layout_width="match_parent" 
  4.     android:background="@android:color/darker_gray" 
  5.     android:layout_height="match_parent" > 
  6.  
  7.     ...    <ViewStub 
  8.         android:id="@+id/error_layout" 
  9.         android:layout_width="wrap_content" 
  10.         android:layout_height="wrap_content" 
  11.         android:layout_gravity="center" 
  12.         android:layout="@layout/error" /></merge> 

在java中通过(ViewStub)findViewById(id)找到ViewStub,通过stub.inflate()展开ViewStub,然后得到子View,如下:

  1. private View errorView; 
  2.  private void showError() { 
  3.     // not repeated infalte 
  4.     if (errorView != null) { 
  5.         errorView.setVisibility(View.VISIBLE); 
  6.         return
  7.     } 
  8.   
  9.     ViewStub stub = (ViewStub)findViewById(R.id.error_layout); 
  10.     errorView = stub.inflate();} 
  11.  private void showContent() { 
  12.     if (errorView != null) { 
  13.         errorView.setVisibility(View.GONE); 
  14.     }} 

在上面showError()中展开了ViewStub,同时我们对errorView进行了保存,这样下次不用继续inflate。

 

总结

这篇Blog没有详细介绍 HierarchyViewer工具的使用,相信如果对布局原则比较熟练之后,对工具的依赖大大减少,开发效率也会大大的提升。除这些布局原则之外,还需 要大家对Android各个组件的属性很熟悉,比如如果要做这么一个布局, 一个图片和一个文本的布局,新手们往往会用一个Layout嵌套ImageView和TextView来做, 但是当我们知道TextView有drawableLeft, drawableRight等属性时,那么实现这样的一个布局是非常快速高效的。总之,且学且实践!

责任编辑:chenqingxiang 来源: oschina
相关推荐

2012-05-08 16:37:23

android

2013-09-17 10:17:39

Android布局

2011-03-24 09:03:13

Android SDKAndroid

2023-08-25 08:06:04

项目布局LazyRow​

2009-12-31 15:21:48

Silverlight

2017-05-11 15:01:43

Androidweb布局

2011-05-31 09:36:46

Android 布局属性

2021-07-29 14:20:34

网络优化移动互联网数据存储

2011-04-07 08:59:47

Android交互设计

2014-09-05 10:10:32

Android自适应布局设计

2016-09-22 09:24:33

AndroidViewStub

2019-07-25 13:22:43

AndroidAPK文件优化

2019-12-13 10:25:08

Android性能优化启动优化

2013-09-16 15:16:20

Android性能优化

2013-03-27 09:17:17

Android开发AndroidList

2010-01-28 11:18:14

Android界面布局

2011-04-22 11:01:36

框架布局界面设计Android

2012-05-14 21:08:47

Android页面布局

2010-10-14 08:55:02

Android布局方式

2011-04-11 17:25:30

线性布局用户界面设计Android
点赞
收藏

51CTO技术栈公众号