Android系统允许我们在布局中嵌套布局。然而,嵌套的布局会增加UI布局阶段花费的时间,从而降低UI的性能。布局层次结构越浅,UI布局阶段花费的时间就越少,从而UI的性能就越好。因此,在满足需求的前提下,我们应该尽量地优化布局层次结构。
你可以使用一些工具来检查你的布局是否需要优化。通常,可以使用以下两种工具来检查布局:
其中,Hierarchy Viewer允许你在应用运行时检查布局层次结构,Lint允许你检查XML布局文件中可能的布局层次结构优化。
Hierarchy Viewer是一个内置在Android Device Monitor中的工具,它允许你在应用运行时查看布局层次结构以及每个View的属性和布局速度。使用这个工具可以帮助你发现布局层次结构导致的性能瓶颈,从而帮助你优化布局层次结构。
具体如何使用Hierarchy Viewer可以阅读 Android性能优化工具之HierarchyViewer 这篇文章。
Lint是一个静态代码检查工具,它可以帮助你检查可能影响应用质量和性能的代码问题。该工具会报告检测到的每个问题并提供问题的描述信息和严重级别,以便你可以快速地确定需要优先进行哪些改进。在使用Android Studio时,配置的Lint和IDE检查会在你编写代码时自动运行。当然,你也可以手动运行Lint。
具体如何使用Lint可以阅读 Android静态代码检查工具Lint 这篇文章。
下面是一些Lint检查的例子:
优化布局层次结构的目标是:使布局变得浅而宽,而不是窄而深。常用的优化策略有以下几种:
开发人员有时会在布局中嵌套多余的布局。例如以下几种场景:
使用LinearLayout来布局有时会加深布局层次结构。例如,考虑如下图所示的布局:
如果使用LinearLayout来布局,那么需要嵌套一层垂直的LinearLayout。示例代码如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_item"
android:layout_width="match_parent"
android:layout_height="@dimen/relative_item_height"
android:padding="@dimen/relative_item_padding"
android:orientation="horizontal">
<ImageView
android:id="@+id/img_album"
android:layout_width="@dimen/relative_img_album_size"
android:layout_height="@dimen/relative_img_album_size"
android:layout_marginRight="@dimen/relative_img_album_margin_right"
android:src="@mipmap/ic_launcher" />
<LinearLayout
android:id="@+id/ll_item_info"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_song_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/relative_tv_song_title_margin_top"
android:textColor="@color/black_333333"
android:textSize="18sp"
android:text="@string/relative_song_title" />
<TextView
android:id="@+id/tv_artist_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/relative_tv_artist_name_margin_top"
android:textColor="@color/gray_7d7d7d"
android:textSize="16sp"
android:text="@string/relative_artist_name" />
LinearLayout>
LinearLayout>
使用Hierarchy Viewer来查看布局层次结构,如下图所示:
如果使用RelativeLayout来布局,那么不需要嵌套一层子布局。示例代码如下所示:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rl_item"
android:layout_width="match_parent"
android:layout_height="@dimen/relative_item_height"
android:padding="@dimen/relative_item_padding">
<ImageView
android:id="@+id/img_album"
android:layout_width="@dimen/relative_img_album_size"
android:layout_height="@dimen/relative_img_album_size"
android:layout_marginRight="@dimen/relative_img_album_margin_right"
android:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/tv_song_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/img_album"
android:layout_marginTop="@dimen/relative_tv_song_title_margin_top"
android:textColor="@color/black_333333"
android:textSize="18sp"
android:text="@string/relative_song_title" />
<TextView
android:id="@+id/tv_artist_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/img_album"
android:layout_below="@id/tv_song_title"
android:layout_marginTop="@dimen/relative_tv_artist_name_margin_top"
android:textColor="@color/gray_7d7d7d"
android:textSize="16sp"
android:text="@string/relative_artist_name" />
RelativeLayout>
使用Hierarchy Viewer查看此时的布局层次结构,如下图所示:
可以看到,使用RelativeLayout来布局时少了一层布局。
产生多余的嵌套布局的另一个常见原因是使用include标签。使用include标签可以重用那些公共的布局。
首先,如果你知道某个布局是可重用的,那么你可以创建一个新的XML文件来定义此布局。例如:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_common"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/common_tv_text_view" />
<Button
android:id="@+id/btn_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="@string/common_btn_button" />
LinearLayout>
该公共的布局在垂直方向上放置了一个TextView和一个Button。
然后,在你想要包含公共布局的布局文件中,使用include标签来包含公共布局。例如:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_merge_demo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/merge_layout_padding"
android:orientation="vertical">
<include layout="@layout/layout_common" />
<TextView
android:id="@+id/tv_hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
LinearLayout>
使用Hierarchy Viewer查看布局层次结构,如下图所示:
对于该例,主布局是一个垂直的LinearLayout,同时公共布局也是一个垂直的LinearLayout,这样就产生了多余的嵌套布局。
为了避免产生一个多余的嵌套布局,你可以使用 merge 标签来作为可重用的公共布局的根标签。例如:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="@+id/tv_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/common_tv_text_view" />
<Button
android:id="@+id/btn_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="@string/common_btn_button" />
merge>
使用Hierarchy Viewer查看此时的布局层次结构,如下图所示:
可以看到,主布局中不再产生多余的嵌套布局了。这是因为,当你使用merge标签时,系统会忽略merge标签,并将所有的子View直接放置在主布局中。
嵌套的布局会增加UI布局阶段花费的时间,从而降低UI的性能。布局层次结构越浅,UI布局阶段花费的时间就越少,从而UI的性能就越好。因此,在满足需求的前提下,我们应该尽量地优化布局层次结构。