zoukankan      html  css  js  c++  java
  • Android布局优化:include 、merge、ViewStub的详细总结

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载。

    本篇博客主要是对上篇博客的补充Android性能优化之UI渲染性能优化, 没有什么新东西,觉得应该是都掌握的玩意,写出来也只是自己做个小小的总结。

    一、include的用法以及注意点

    在开发Android布局时,我们常将一些通用的视图提取到一个单独的layout文件中,然后使用<include>标签在需要使用的其他layout布局文件中加载进来,比如我们自己App导航栏等。这样,便于对相同视图内容进行统一的控制管理,提高布局重用性。

    下面我们以大部分项目中都有的头部导航栏为例,说明一下include的使用,比如我们项目自己统一头部导航栏,抽取布局如下:

    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"  
    12         android:text="返回按钮" />  
    13   
    14     <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="关闭按钮" />  
    29 
    30 </RelativeLayout>

    很简单,就是左右各一个按钮,中间是一个提示文字。使用也比较简单,如下:

    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>
    include标签使用还是很简单的,主要通过layout属性声明要引入的布局即可。运行程序界面如下:

    include标签使用注意点:
    1,<include>标签当中,可以重写所有layout属性的,如上面include中指定的layout属性将会覆盖掉titlebar中指定的layout属性。
    而非layout属性则无法在<include>标签当中进行覆写。另外需要注意的是,如果我们想要在<include>标签当中覆写layout属性,
    必须要将layout_width和layout_height这两个属性也进行覆写,否则覆写效果将不会生效
    2,一个xml布局文件有多个include标签需要设置ID,才能找到相应子View的控件,否则只能找到第一个include的layout布局,以及该布局的控件
    3,如果我们给include所加载的layout布局的根容器设置了id属性,也在include标签中设置了id属性,同时需要在代码中获取根容器的控件对象
    时,最好将这两个id设置相同的名称!否则,可能获取不到根容器对象,即为null。

    二、merge的用法以及注意点

    merge标签存在的意义是帮助include标签排除多余的一层ViewGroup容器,减少view hierarchy的结构,提升UI渲染的性能。include标签存在着一个不好的地方,可能会导致产生多余的布局嵌套。同样通过一个小demo来说明:

    比如项目中有一个公共的登录按钮布局,如下:

    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"  
    12         android:text="登录按钮" />  
    13     
    14 </LinearLayout>

    很简单,就是一个登录的Button。

    项目中有登录功能的UI界面如下:

    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使用的总结,希望对你有用,即使已经掌握,希望读完此文能温故知新。

  • 相关阅读:
    Android Media Playback 中的MediaPlayer的用法及注意事项(二)
    Android Media Playback 中的MediaPlayer的用法及注意事项(一)
    34. Search for a Range
    33. Search in Rotated Sorted Array
    32. Longest Valid Parentheses
    31. Next Permutation下一个排列
    30. Substring with Concatenation of All Words找出串联所有词的子串
    29. Divide Two Integers
    28. Implement strStr()子串匹配
    27. Remove Element
  • 原文地址:https://www.cnblogs.com/leipDao/p/8981687.html
Copyright © 2011-2022 走看看