Android 中“Layout”布局,主要有如下几个方面:
1、布局UI设计,即如何摆放UI,UI呈现效果等
2、布局文件 ,即/res/layout/xxx.xml;
3、布局过程 ,Android绘制过程中的 layout过程;
4、一些布局控件,例如LinearLayout、FrameLayout等。
Android SDK 及它包含的工具都能帮助你定位在布局过程中隐藏的问题,你能够以很小的内存代价去实现流畅的平滑界面。
如下例子说明布局优化的效果:
优化布局层次
一个通常的错误观念就是使用基本的布局结构(例如:LinearLayout、FrameLayout等)能够在大多数情况下产生高效率 的布局。 显然,你的应用程序里添加的每一个控件和每一个布局都需要初始化、布局(layout)、 绘制 (drawing)。举例来说:嵌入一个LinearLayout会产生一个太深的布局层次。更严重的是,嵌入几个使用layout_weight属性的LinearLayout 将会导致大量的开销,因为每个子视图都需要被测量两次。这是反复解析布局文件时重要的一点,例如在ListView或者GridView中使用时。
2、 观察你的布局
Android SDK 工具箱包括一个称作“ Hierarchy Viewer”的工具,它允许你去在你的应用程序运行时分析布局。通过使用这个工具,能帮助你发现你的布局效率上的瓶颈问题。
“ Hierarchy Viewer”工具允许你在已连接的设备或模拟器中选择正在运行的进程,然后呈现出布局层次树 (layout tree)。
每个正方块下的交通灯(见下图) :
--- 红绿蓝----
图1:色块标识
图1表现出了在测量(measure)、布局(layout)、以及绘制 (draw)过程中的效率值,这能帮助你定位潜在的问题。
“Hierarchy Viewer”工具可以在 <sdk>/tools路径下找到。当打开它时,“ HierarchyViewer”工具显示了所有可用的设备以及运行在这些设备上的进程。点击”Load View Hierarchy”来显示某个你选择的组件的UI布局层次。举例来说,图2展现了图1的布局层次树。
/ ImageView
LinearLayout - LinearLayout - TextView
\ TextView
图2:使用LinearLayout的布局树
在图2中,你可以直观看到这个三层的布局结构是存在一些问题的。点击项体现出了在每个测量(measure)、布局(layout)、以及绘制(draw)过程中的时间消耗(见图3)。很明显,该项(LinearLayout)花费了最长的时间去测量、布局、绘制,应该花点精力去优化它们。
LinearLayout @1245fed
5views
Measure:0673ms
Layout:0.234ms
Draw:3.344ms
图3: 某个LinearLayout的绘制时间
完成该布局文件渲染的时间分别为:测量过程:0.977ms 布局过程: 0.167ms 绘制过程:2.717ms
3、 修改布局文件
由于上图中布局效率的低下是因为一个内嵌的 LinearLayout控件,通过扁平化布局文件----让布局变得更浅更宽,而不是变得更窄更深层次,这样就能提升效率了。 一个RelativeLayout 作为根节点也能提供如上的布局效果(即图1)。 因此,使用RelativeLayout改变布局的设计,你可以看到现在我们的布局层次只有2层了。 新的布局层次树如下:
/ ImageView
RelativeLayout - TextView
\ TextView
图4:使用RelativeLayout的布局树
可能它只是一点点微小的改进,但这次它会被多次调用,因为是ListView会布局所有的Item,累积起来, 改进后效果还是非常可观地。
大部分的时间差异是由于使用了带有layout_weight 属性的LinearLayout ,它能减缓测量过程的速度。这仅仅是一个例子,即每个布局都应该合适地被使用以及你应该认真考虑是否有必要采用“layout_weight" 属性。
Android 中的lint工具,它可以自动分析你的布局,发现可能并不需要的布局元素,以降低布局复杂度。(以前是 layoutopt工具,可以解决无用布局、嵌套太多、布局太多)
Lint规则如下:
1、使用组合控件 ---包含了一个 ImageView以及一个 TextView控件的 LinearLayout如果能够作为一个组合控件将会被更有效的处理。
2、合并作为根节点的帧布局(Framelayout) ----如果一个帧布局时布局文件中的根节点,而且它没有背景图片或者padding等,更有效的方式是使用<merge />标签替换该< Framelayout/>标签。
3、无用的子节点-----通常来说如果一个布局控件没有子视图或者背景图片,那么该布局控件时可以被移除 (由于它处于 invisible状态)。
4、无用的父节点 ----- 如果一个父视图即有子视图,但没有兄弟视图节点,该视图不是ScrollView控件或者根节点,并且它没有背景图片,也是可以被移除的,移除之后,该父视图的所有子视图都直接迁移至之前父视图的布局层次。同样能够使解析布局以及布局层次更有效。
5、过深的布局层次 ----内嵌过多的布局总是低效率地。考虑使用一些扁平的布局控件,例如 RelativeLayout、GridLayout,来改善布局过程。默认最大的布局深度为10。
在布局设计期间,我们会频繁地移动各种组件,有些组件最终可能会不再使用,如:
1. <?xml version="1.0" encoding="utf-8"?>
2. <LinearLayout
3. xmlns:android="http://schemas.android.com/apk/res/android"
4. android:layout_width="match_parent"
5. android:layout_height="match_parent"
6. android:orientation="horizontal">
7. <LinearLayout
8. android:id="@+id/linearLayout1"
9. android:layout_height="wrap_content"
10. android:layout_width="wrap_content"
11. android:orientation="vertical">
12. <TextView
13. android:id="@+id/textView1"
14. android:layout_width="wrap_content"
15. android:text="TextView"
16. android:layout_height="wrap_content"></TextView>
17. </LinearLayout>
18. </LinearLayout>
工具将会很快告诉我们LinearLayout内的LinearLayout是多余的:
11:17 This LinearLayout layout or its LinearLayout parent is useless
输出结果每一行最前面的两个数字表示建议的行号。
根可以替换
Layoutopt的输出有时是矛盾的,例如:
1. <?xml version="1.0" encoding="utf-8"?>
2. <FrameLayout
3. xmlns:android="http://schemas.android.com/apk/res/android"
4. android:layout_width="match_parent"
5. android:layout_height="match_parent">
6. <LinearLayout
7. android:id="@+id/linearLayout1"
8. android:layout_height="wrap_content"
9. android:layout_width="wrap_content"
10. android:orientation="vertical">
11. <TextView
12. android:id="@+id/textView1"
13. android:layout_width="wrap_content"
14. android:text="TextView"
15. android:layout_height="wrap_content"></TextView>
16. <TextView
17. android:text="TextView"
18. android:id="@+id/textView2"
19. android:layout_width="wrap_content"
20. android:layout_height="wrap_content"></TextView>
21. </LinearLayout>
22. </FrameLayout>
这个布局将返回下面的输出:
1. 5:22 The root-level <FrameLayout/> can be replaced with <merge/>
2. 10:21 This LinearLayout layout or its FrameLayout parent is useless
第一行的建议虽然可行,但不是必需的,我们希望两个TextView垂直放置,因此LinearLayout应该保留,而第二行的建议则可以采纳,可以删除无用的FrameLayout。
太多的视图
每个视图都会消耗内存,在一个布局中布置太多的视图,布局会占用过多的内存,假设一个布局包含超过80个视图,layoutopt可能会给出下面这样的建议:
1. -1:-1 This layout has too many views: 83 views, it should have <= 80!
2. -1:-1 This layout has too many views: 82 views, it should have <= 80!
3. -1:-1 This layout has too many views: 81 views, it should have <= 80!
嵌套太多
布局不应该有太多的嵌套,layoutopt(和Android团队)建议布局保持在10级以内,即使是最大的平板电脑屏幕,布局也不应该超过10级,RelativeLayout可能是一个解决办法,但它的用法更复杂,好在Eclipse中的Graphical Layout资源工具更新后,使得这一切变得更简单。
下面是布局嵌套太多时,layoutopt的输出内容:
1. -1:-1 This layout has too many nested layouts: 12 levels, it should have <= 10!
2. 305:318 This LinearLayout layout or its RelativeLayout parent is possibly useless
3. 307:314 This LinearLayout layout or its FrameLayout parent is possibly useless
4. 310:312 This LinearLayout layout or its LinearLayout parent is possibly useless
嵌套布局警告通常伴随有一些无用布局的警告,有助于找出哪些布局可以移除,避免屏幕布局全部重新设计。