美文网首页Android开发经验谈程序员Android技术知识
Android布局优化:include 、merge、ViewS

Android布局优化:include 、merge、ViewS

作者: 06fd4cf1f427 | 来源:发表于2019-06-04 20:30 被阅读34次

    一、include的用法以及注意点

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

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

    titlebar.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="match_parent" >
        
         <Button  
            android:id="@+id/back"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:layout_alignParentLeft="true"  
            android:layout_centerVertical="true"  
            android:text="返回按钮" />  
      
        <TextView  
            android:id="@+id/title"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:layout_centerInParent="true"  
            android:text="提示文字"  
            android:textSize="20sp" />  
      
        <Button  
            android:id="@+id/close"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:layout_alignParentRight="true"  
            android:layout_centerVertical="true"  
            android:text="关闭按钮" />  
    
    </RelativeLayout>
    

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

    activity_main.xml:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="${relativePackage}.${activityClass}" >
    
        <include
            android:layout_width="match_parent"
            android:layout_height="40dp"
            layout="@layout/titlebar" />
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_centerVertical="true"
            android:onClick="click"
            android:text="点我。。。" />
    
    </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:

    <?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="wrap_content"
        android:orientation="vertical" >
        
         <Button 
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:layout_marginLeft="20dp"  
            android:layout_marginRight="20dp"  
            android:text="登录按钮" />  
        
    </LinearLayout>
    

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

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

    activity_login.xml:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="@android:color/holo_blue_light">
    
        <EditText   
            android:layout_width="match_parent"  
            android:layout_height="wrap_content" 
            android:layout_marginLeft="20dp"  
            android:layout_marginRight="20dp"  
            android:layout_marginTop="40dp"  
            android:hint="请输入用户名" />
        
        <include layout="@layout/login" />
    
    </LinearLayout>
    

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

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

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

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

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

    <?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="http://schemas.android.com/apk/res/android">
        
         <Button 
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:layout_marginLeft="20dp"  
            android:layout_marginRight="20dp"  
            android:text="登录按钮" />  
        
    </merge>
    

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

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

    merge标签使用注意点:

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

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

    LayoutInflate中源码体现:

    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
            synchronized (mConstructorArgs) {
                
                ...
    
                    if (TAG_MERGE.equals(name)) {
                        if (root == null || !attachToRoot) {
                            throw new InflateException("<merge /> can be used only with a valid "
                                    + "ViewGroup root and attachToRoot=true");
                        }
    
                        rInflate(parser, root, inflaterContext, attrs, false);
                    }
                ...
            }
        }
    

    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:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:layout_height="wrap_content" >
    
        <EditText
            android:id="@+id/weichat_id"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:layout_marginTop="10dp"
            android:hint="请输入微信号" />
    
        <EditText
            android:id="@+id/address_id"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:layout_marginTop="10dp"
            android:hint="请输入家庭住址" />
    
    </LinearLayout>
    

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

    activity_main.xml:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_blue_light"
        android:orientation="vertical" >
    
        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:layout_marginTop="40dp"
            android:hint="请输入用户名" />
    
        <ViewStub
            android:id="@+id/viewstub"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout="@layout/otherinfo" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="show"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:layout_marginTop="10dp"
            android:text="显示" />
    
    </LinearLayout>
    

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

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

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

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

    activity中代码如下:

    public void show(View view){
            //
            ViewStub stub = ((ViewStub) findViewById(R.id.viewstub));
            if(stub!=null){
                View stubView = stub.inflate();
                EditText address = (EditText) stubView.findViewById(R.id.address_id);  
                EditText wechatId = (EditText) stubView.findViewById(R.id.weichat_id);  
            }
        }
    

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

    点击显示按钮,UI如下:

    ViewStub标签使用注意点:

    1,ViewStub标签不支持merge标签。因此这有可能导致加载出来的布局存在着多余的嵌套结构,具体如何去取舍就要根据各自的实际情况来决定了。
    2,ViewStub的inflate只能被调用一次,第二次调用会抛出异常。
    3,虽然ViewStub是不占用任何空间的,但是每个布局都必须要指定layout_width和layout_height属性,否则运行就会报错。

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

    如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

    最后针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

    需要展开的架构学习笔记导图的加群免费获取 Android架构设计大群(185873940)

    PS:群内有许多技术大牛,高手如云,有任何问题,欢迎广大网友一起来交流,群内还不定期免费分享高阶Android学习视频资料和面试资料包~

    相关文章

      网友评论

        本文标题:Android布局优化:include 、merge、ViewS

        本文链接:https://www.haomeiwen.com/subject/pfxkxctx.html