布局是Android应用中能直接影响到用户体验的关键部分,如果使用不当,可能会导致应用界面卡顿并且占用大量的内存。
因此我们在编写布局时应该注意如下几点:
- 优化布局的层次结构
- 通过<include />重复使用布局
- 通过<ViewStub />实现按需加载视图
1. 优化布局的层次结构
编写布局时,我们应该遵循如下规则,防止给系统测量布局带来额外的计算量,从而造成UI卡顿或占用内存过多的情况出现:
(1)尽量展平布局,避免出现视图嵌套过深的情况;
(2)尽量避免使用layout_weight,这会减缓测量布局的速度;
2. 通过<include />重复使用布局
通过使用<include />标签,我们能够复用视图结构类似的布局,例如所有页面的公共头部,下面是一个具体的例子:
假设我们当前存在一个公共的头部视图(左侧图片,中间标题),其布局文件为 top_header_layout.xml:
<androidx.constraintlayout.widget.ConstraintLayout 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="50dp"
android:background="@color/com_color_white">
<ImageView
android:id="@+id/img_my_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/com_16dp"
android:paddingTop="@dimen/com_5dp"
android:paddingRight="@dimen/com_16dp"
android:paddingBottom="@dimen/com_5dp"
android:src="@mipmap/com_icon_back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_my_center"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text=""
android:textSize="@dimen/com_18sp"
android:textStyle="bold"
android:textColor="@color/com_color_black333"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
如果不使用<include />标签,我们就需要在每个layout文件中加入以上代码,这不仅产生了冗余的代码,而且倘若日后UI图发生变化,也不方便维护。
使用<inclue />标签的方式也十分简单,我们只需要在其layout属性中指定待复用加载的布局资源文件即可。例如:
<include
android:id="@+id/header"
layout="@layout/top_header_layout" />
接着在布局对应的Activity中,通过<include />标签指定的id获取到对应的资源实例:
View header = findViewById(R.id.header);
ImageView headerLeftIv = header.findViewById(R.id.img_my_left);
TextView headerCenterTv = header.findViewById(R.id.tv_my_center);
此外,在include上也可以设置View的相关属性,这些属性会替换复用布局中根视图的相关属性。这些属性生效的前提是必须在<include />标签中同时替换 android:layout_height 和 android:layout_width 属性。
<merge />标签:该标签常常配合<include />标签使用,当我们不希望在可复用视图中引入额外的根布局而增加视图的嵌套层级时,就可以使用<merge />作为根标签(类似于React中的<></>)。该标签在引入到目标布局中时会被忽略。
<merge />标签并不是一个View视图,所以在这个标签上是无法设置View相关属性的。使用例子如下:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/add"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/delete"/>
</merge>
3. 通过<ViewStub />实现按需加载视图
在我们初学布局时,一般都会通过visibility这个属性来实现视图的显示和隐藏。但是如果某个视图在大部分情况下都不显示,采用此种方式,为一个在少数特定情况下才会显示的视图去进行布局的计算显然是没有必要的。所以,我们应该学会使用ViewStub来实现视图的按需加载、延迟加载。
ViewStub是一种没有任何维度的轻量的视图,它不会绘制任何内容或参与布局(可以理解为一种不会带来计算消耗的占位符)。基本使用方式如下:
<!-- android:id 当前ViewStub标签的资源id -->
<!-- android:inflatedId layout动态加载后使用的id -->
<!-- android:layout 指定需要动态加载的layout -->
<ViewStub
android:id="@+id/stub_import"
android:inflatedId="@+id/panel_import"
android:layout="@layout/progress_overlay" //
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" />
(备注:android:inflatedId:在ViewStub被动态替换为具体的视图后,该视图所使用的id)
接着在代码中,通过以下两种方式,我们可以动态的加载视图到ViewStub对应的位置中:
ViewStub stub = findViewById(R.id.stub_import);
// 方式一
stub.setVisibility(View.VISIBLE);
View replaceView = findViewById(R.id.panel_import);
// 方式二
View replaceView = stub.inflate();