一、include布局
页面逐渐多时,会存在多个页面含有同一个布局的情况,也就数多个页面公用了一些UI组件,例如自定义标题栏,每个页面使用相同的统一的标题栏使各个页面标题效果基本保持一致,这个标题栏就是各个页面的公共组件,如果每个页面重复定义这个标题布局,修改时会带来大量重复工作,针对这种场景,Android提供了include标签,用于将一个布局引入到一个布局中
我们自定义一个标题栏,将这个标题栏独立到title.xml布局中:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:gravity="center"
android:text="<"
android:textSize="18sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="标题"
android:textSize="18sp"/>
</RelativeLayout>
标题栏由两个文本空间组成,用“<”表示返回键,空间显示如图:
下面我们就将这个标题栏引入到一个Activity的xml中:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/top_title"
layout="@layout/title"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/common_google_signin_btn_icon_dark"/>
</FrameLayout>
通过include标签引入title布局得到结果如下图所示:
这样一来就复用了title.xml标题栏效果,降低了维护成本也提升了代码的复用率,include实现原理就是在解析xml文件时,如果检测到include标签,那么直接把该布局下的根视图添加到include所在的父视图中。
二、merge标签
布局中我们经常需要处理嵌套问题,有一种情况是子布局的根视图和他父视图是同一类型,布局xml中的内容是通过setContentView函数设置给Activity的,实际上是设置给了Activity布局中id为content的FrameLayout视图,我们设置的xml布局只是其中的一部分,Android系统会在Framework层封装这个逻辑,也就是说Activity内容视图的顶层布局是FrameLayout,在include的例子中,Activity的xml也是FrameLayout,这样就会产生冗余视图,可以通过HierarchyViewer工具来查看UI资源的分配情况,这种情况下就需要用merge标签作为子视图的顶级视图来解决掉多余的层级。我们将根视图替换为merge标签:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/top_title"
layout="@layout/title"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/common_google_signin_btn_icon_dark"/>
</merge>
三、ViewStub视图
Viewstub视图是一个不可见的视图,能在运行期间延迟加载,宽和高都为0,当对一个ViewStub设置它可见时,系统会加载在ViewStub中指定的布局,将布局的根视图添加到ViewStub的父视图中,ViewStub在设置为visible之前,是不占用布局空间和系统资源的,只是为目标视图占位。如下使用ViewStub标签动态加载TextView。
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/top_title"
layout="@layout/title"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/common_google_signin_btn_icon_dark"/>
<ViewStub
android:id="@+id/stub_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout="@layout/stub_view"/>
</merge>
TextView如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="stub view"
android:textSize="18sp"/>
</LinearLayout>
默认情况下不会加载Textview视图,只会通过ViewStub来占位,可以动态加载使TextView可见:
ViewStub vs = (ViewStub)findViewById(R.id.stub_view);
vs.inflate();
vs.setVisibility(View.VISIBLE);
四、减少视图树的层级
每一个视图在显示时都会经历测量、布局、绘制的过程,如果布局中嵌套的视图层次过多,那么就会造成额外测量、布局等工作,使UI变的卡顿。主要遵守的原则包括:
(1)尽量多使用RelativeLayout,不要使用AbsoluteLayout;
(2)在ListView中避免使用LinearLayout的Layout_weight属性;
(3)将复用的组件抽取出来通过<include/>标签使用
(4)使用<ViewStub/>标签来加载不常用的布局
(5)使用<merge/>标签减少布局的嵌套层次