在布局优化中,Androi的官方提到了这三种布局include、merge、ViewStub下面我们对这三种布局的使用和注意事项进行说明:
include标签是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="wrap_content">
<TextView android:id="@+id/tvTitle" android:layout_width="match_parent" android:layout_height="72dp" android:gravity="center" android:background="@android:color/holo_blue_dark" android:textColor="@android:color/white" android:textSize="18sp" android:text="Title"/>
</RelativeLayout>
效果如下:
假设我们有10个页面,那我们就要重复写十遍这样的代码?如果更改标题栏的样式呢?我们还要每个页面都修改一次?想想太可怕了。放心,include标签不会让你这么做的。接下来我们使用include标签来试试这个需求。
<?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">
<include layout="@layout/title_layout"/>
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_red_dark">
</LinearLayout>
</LinearLayout>
我们在Activity的布局中使用include标签来引用了title_layout,效果如下:
使用include注意事项:
View include1 = findViewById(R.id.include1);
View include2 = findViewById(R.id.include2);
TextView textView1 = (TextView) include1.findViewById(R.id.tvTitle);
TextView textView2 = (TextView) include2.findViewById(R.id.tvTitle);
textView1.setText("textView1");
textView2.setText("textView2");
merge标签在UI的结构优化中起着非常重要的作用,它可以删减多余的层级,优化UI。merge多用于替换FrameLayout或者当一个布局包含另一个时,merge标签消除视图层次结构中多余的视图组。例如你的主布局文件是垂直布局,引入了一个垂直布局的include,这是如果include布局使用的LinearLayout就没意义了,使用的话反而减慢你的UI表现。这时可以使用merge标签优化。
我们通过代码来演示一下:
layout_merge.xml
<?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="match_parent" android:layout_height="wrap_content" android:text="11111111111"/>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="11111111111"/>
</LinearLayout>
这是一个垂直的LinearLayout布局,这个布局将会被include引用到另外一个布局中。
activity_layout.xml
<?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">
<include layout="@layout/layout_merge"/>
</LinearLayout>
同样是一个垂直的LinearLayout布局,在这其中使用include标签引用了layout_merge布局,我们运行之后通过Hierarchy Viewer看一下布局的层次。
从图中我们看到,两个LinearLayout明显是冗余的,这个时候我们就可以使用merge标签来优化了。我们来具体操作一下。
我们只需要将layout_merge.xml的根布局LinearLayout改成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" android:orientation="vertical">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="11111111111"/>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="11111111111"/>
</merge>
运行后,使用Hierarchy Viewer来查看布局层次如下:
我们可以看到少了一个LinearLayout。这就是merge的作用。
当使用include标签来添加一个根布局为merge标签的布局时,系统会忽略merge结点,并将其中的内容直接添加到include标签所在的布局中。
注意事项:
1.merge标签只能是一个文件的根结点,然后使用include标签在其他文件中引用带有merge标签的文件。
2.如果是activity的布局,并且根结点是FrameLayout的布局,也可以使用merge来替代FrameLayout。(因为我们activity的布局会被系统添加到id为content的FrameLayout中去)
ViewStub标签最大的优点是当你需要时才会加载,使用他并不会影响UI初始化时的性能。各种不常用的布局想进度条、显示错误消息等可以使用ViewStub标签,以减少内存使用量,加快渲染速度。ViewStub 是一个不可见的,大小为0的View。ViewStub 标签使用如下:
我们这里使用上面的layout_merge.xml进行演示:
<?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:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="11111111111" />
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="22222222" />
</LinearLayout>
我们看到这儿LinearLayout中有两个TextView,在看看如何使用ViewStub:
<?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">
<Button android:id="@+id/btnShowViewStub" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="显示viewStub"/>
<ViewStub android:id="@+id/viewStub" android:layout_width="match_parent" android:layout_height="match_parent" android:inflatedId="@+id/layoutViewStub" android:layout="@layout/layout_merge"/>
</LinearLayout>
这里有一个button和一个ViewStub,ViewStub标签使用android:layout属性来引用要延迟加载的布局。这里指定了inflatedId为layoutViewStub。
其中inflatedId就是新加载进来的view的id,如果需要获取这个view,就要用这个inflatedId,原来的id已经被取代了
在看看Java代码中的操作。
ViewStub viewStub;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
viewStub = (ViewStub) findViewById(R.id.viewStub);
findViewById(R.id.btnShowViewStub).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 加载(显示)viewStub对应的布局,并接受返回的View对象
View view = viewStub.inflate();
// 获取返回View对象中的TextView1控件
TextView textView = (TextView) view.findViewById(R.id.textView1);
// 修改TextView文本
textView.setText("修改TextView1");
// 显示ViewStub中的内容,但是不会反回View对象
// viewStub.setVisibility(View.VISIBLE);
}
});
}
当调用inflate()函数的时候,ViewStub被引用的资源替代,并且返回引用的view。 这样程序可以直接得到引用的view而不用再次调用函数findViewById()来查找了。
需要注意的是ViewStub只能inflate一次,反复的inflate会报错:java.lang.IllegalStateException:ViewStub must have a non-null ViewGroup viewParent。