Android layout optimization: a detailed summary of include, merge, ViewStub

Copyright statement: This article is from Wang Lei's blog, and it is prohibited to reprint without the author's permission.

This blog is mainly to supplement the previous blog 's UI rendering performance optimization of Android performance optimization. There is nothing new. I think it should be something that everyone knows, and I just write it out to make a small summary.

First, the usage and attention of include

When developing Android layouts, we often extract some common views into a separate layout file, and then use <include>tags to load them in other layout files that need to be used, such as our own App navigation bar. In this way, it is convenient to perform unified control and management on the same view content, and improve the layout reusability.

Let's take the header navigation bar in most projects as an example to illustrate the use of include. For example, our project unifies the header navigation bar, and the extraction layout is as follows:

titlebar.xml:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent" >
 5     
 6      <Button  
 7         android:id="@+id/back"  
 8         android:layout_width="wrap_content"  
 9         android:layout_height="wrap_content"  
10         android:layout_alignParentLeft="true"  
11         android:layout_centerVertical="true"  
1413         android:text="Back button" />  
12   
     <TextView  
15         android:id="@+id/title"  
16         android:layout_width="wrap_content"  
17         android:layout_height="wrap_content"  
18         android:layout_centerInParent="true"  
19         android:text="提示文字"  
20         android:textSize="20sp" />  
21   
22     <Button  
23         android:id="@+id/close"  
24         android:layout_width="wrap_content"  
25         android:layout_height="wrap_content"  
26         android:layout_alignParentRight="true"  
27         android:layout_centerVertical="true"  
 28          android:text="Close button" />  
 29  
30 </RelativeLayout>

It is very simple, it is a button on the left and right, and a prompt text in the middle. It is also relatively simple to use, as follows:

activity_main.xml:

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     tools:context="${relativePackage}.${activityClass}" >
 6 
 7     <include
 8         android:layout_width="match_parent"
 9         android:layout_height="40dp"
10         layout="@layout/titlebar" />
11 
12     <Button
13         android:layout_width="wrap_content"
14         android:layout_height="wrap_content"
15         android:layout_centerHorizontal="true"
16         android:layout_centerVertical="true"
17         android:onClick="click"
18         android:text="点我。。。" />
19 
20 </RelativeLayout>
The use of the include tag is still very simple, and the layout to be imported can be declared mainly through the layout attribute. The running program interface is as follows: 

Notes on the use of include tags:
1. In the <include> tag, all layout attributes can be rewritten. For example, the layout attribute specified in the include above will override the layout attribute specified in the titlebar.
The non-layout attribute cannot be overridden in the <include> tag. Another thing to note is that if we want to overwrite the layout attribute in the <include> tag, we
must also overwrite the two attributes layout_width and layout_height, otherwise the overriding effect will not take effect
2, an xml layout file There are multiple include tags that need to set IDs to find the controls of the corresponding child View, otherwise only the layout layout of the first include and the controls of the layout can be found .
3. If we set the id attribute for the root container of the layout loaded by include, and also set the id attribute in the include tag, and need to get the control object
of the root container in the code, it is best to set these two ids. same name! Otherwise, the root container object may not be obtained, which is null.

Second, the usage and attention of merge

mergeThe meaning of the existence of the label is to help the includelabel to exclude an extra layer of ViewGroup container, reduce the structure of the view hierarchy, and improve the performance of UI rendering. The include tag has a bad place, which may lead to redundant layout nesting. Also through a small demo to illustrate:

For example, there is a public login button layout in the project, as follows:

login.xml:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="wrap_content"
 5     android:orientation="vertical" >
 6     
 7      <Button 
 8         android:layout_width="match_parent"  
 9         android:layout_height="wrap_content"  
10         android:layout_marginLeft="20dp"  
11         android:layout_marginRight="20dp"  
1413         android:text="login button" />  
12     
</LinearLayout>

Very simple, it is a login Button.

The UI interface with the login function in the project is as follows:

activity_login.xml:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical"
 6     android:background="@android:color/holo_blue_light">
 7 
 8     <EditText   
 9         android:layout_width="match_parent"  
10         android:layout_height="wrap_content" 
11         android:layout_marginLeft="20dp"  
12         android:layout_marginRight="20dp"  
13         android:layout_marginTop="40dp"  
14         android:hint="请输入用户名" />
15     
16     <include layout="@layout/login" />
17 
18 </LinearLayout>

同样非常简单,运行程序,如下:

 

看起来没什么问题,其实不知不觉中我们多嵌套了一层布局。我们用工具查看一下此时布局结构:

除去系统布局,我们自己布局最外层是LinearLayout,然后两个并列布局EditText与LinearLayout,在LinearLayout里面是Button登录按钮。

其实这种情况下:在主界面中,<include>标签的parent ViewGroup与包含的layout根容器ViewGroup是相同的类型,这里都是LinearLayout,那么则可以将包含的layout根容器ViewGroup使用<merge>标签代替,从而减少一层ViewGroup的嵌套,提升UI渲染性能。

这里我们把activity_login.xml修改如下:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <merge xmlns:android="http://schemas.android.com/apk/res/android">
 3     
 4      <Button 
 5         android:layout_width="match_parent"  
 6         android:layout_height="wrap_content"  
 7         android:layout_marginLeft="20dp"  
 8         android:layout_marginRight="20dp"  
 9         android:text="登录按钮" />  
10     
11 </merge>

重新运行程序UI和上面一样效果,通过工具再次查看布局结构;

看到了吧,我们自己布局减少了一层嵌套,从而提升了UI的渲染速度。

merge标签使用注意点:

1,根布局是FrameLayout且不需要设置background或padding等属性,可以用merge代替,因为Activity的ContentView父元素就是FrameLayout,所以可以用merge消除只剩一个.

2,因为merge标签并不是View,所以在通过LayoutInflate.inflate()方法渲染的时候,第二个参数必须指定一个父容器,且第三个参数必须为true,也就是必须为merge下的视图指定一个父亲节点.由于merge不是View所以对merge标签设置的所有属性都是无效的.

LayoutInflate中源码体现:

 1     public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
 2         synchronized (mConstructorArgs) {
 3             
 4             ...
 5 
 6                 if (TAG_MERGE.equals(name)) {
 7                     if (root == null || !attachToRoot) {
 8                         throw new InflateException("<merge /> can be used only with a valid "
 9                                 + "ViewGroup root and attachToRoot=true");
10                     }
11 
12                     rInflate(parser, root, inflaterContext, attrs, false);
13                 }
14             ...
15         }
16     }

3,merge标签必须使用在根布局,并且ViewStub标签中的layout布局不能使用merge标签.

ViewStub的用法以及注意点

ViewStub也可以用来加载布局文件,但与include标签完全不同。ViewStub是一个不可见的View类,用于在运行时按需懒加载资源,只有在代码中调用了viewStub.inflate()或者viewStub.setVisible(View.visible)方法时才内容才变得可见。这里需要注意的一点是,当ViewStub被inflate到parent时,ViewStub就被remove掉了,即当前view hierarchy中不再存在ViewStub,而是使用对应的layout视图代替。

同样我们通过一个小demo说明一下,比如我们需要保存一个用户信息,用户名是必须保存的,但是其余信息是不必要的,这是其余信息就可以一开始不显示出来,用户想输入的时候在现实出来。

其余信息布局如下:

otherinfo.xml:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:orientation="vertical"
 5     android:layout_height="wrap_content" >
 6 
 7     <EditText
 8         android:id="@+id/weichat_id"
 9         android:layout_width="match_parent"
10         android:layout_height="wrap_content"
11         android:layout_marginLeft="20dp"
12         android:layout_marginRight="20dp"
13         android:layout_marginTop="10dp"
14         android:hint="请输入微信号" />
15 
16     <EditText
17         android:id="@+id/address_id"
18         android:layout_width="match_parent"
19         android:layout_height="wrap_content"
20         android:layout_marginLeft="20dp"
21         android:layout_marginRight="20dp"
22         android:layout_marginTop="10dp"
23         android:hint="请输入家庭住址" />
24 
25 </LinearLayout>

很简单,没什么其余解释的,主界面布局如下:

activity_main.xml:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:background="@android:color/holo_blue_light"
 6     android:orientation="vertical" >
 7 
 8     <EditText
 9         android:layout_width="match_parent"
10         android:layout_height="wrap_content"
11         android:layout_marginLeft="20dp"
12         android:layout_marginRight="20dp"
13         android:layout_marginTop="40dp"
14         android:hint="请输入用户名" />
15 
16     <ViewStub
17         android:id="@+id/viewstub"
18         android:layout_width="match_parent"
19         android:layout_height="wrap_content"
20         android:layout="@layout/otherinfo" />
21 
22     <Button
23         android:layout_width="match_parent"
24         android:layout_height="wrap_content"
25         android:onClick="show"
26         android:layout_marginLeft="20dp"
27         android:layout_marginRight="20dp"
28         android:layout_marginTop="10dp"
29         android:text="显示" />
30 
31 </LinearLayout>

其余信息界面通过ViewStub引入进来,关于ViewStub主要属性以及方法说明如下:

    • android:layout属性
      加载包含的layout布局文件;

    • android:inflatedId属性
      重写包含的layout布局文件的根容器id;

    • inflate()方法
      setVisible(int)方法作用类似,都可以使内容得以显示,只是inflate()会返回一个View对象,避免了额外使用findViewById()方法获取layout视图对象。

activity中代码如下:

1 public void show(View view){
2         //
3         ViewStub stub = ((ViewStub) findViewById(R.id.viewstub));
4         if(stub!=null){
5             View stubView = stub.inflate();
6             EditText address = (EditText) stubView.findViewById(R.id.address_id);  
7             EditText wechatId = (EditText) stubView.findViewById(R.id.weichat_id);  
8         }
9     }

好了,运行程序,一开始如下:

点击显示按钮,UI如下:

 ViewStub标签使用注意点:

1,ViewStub标签不支持merge标签。因此这有可能导致加载出来的布局存在着多余的嵌套结构,具体如何去取舍就要根据各自的实际情况来决定了。

2,ViewStub的inflate只能被调用一次,第二次调用会抛出异常。

3,虽然ViewStub是不占用任何空间的,但是每个布局都必须要指定layout_width和layout_height属性,否则运行就会报错。

好了,以上就是个人对于include 、merge、ViewStub使用的总结,希望对你有用,即使已经掌握,希望读完此文能温故知新。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325222534&siteId=291194637