Android布局优化:include 、merge、ViewStub标签详解

前言

在日常开发中,一个项目里可能会有很多相似的布局,如果我们每一个XML文件都写一次,不说麻烦,代码也显得冗余,而且可读性也很差。这时候我们可以将相同的布局提取出来重复使用。

Tips

使用Android Studio可以直接在布局文件对应控件:
右键 -> Refactor -> Extract -> Style 抽取样式
右键 -> Refactor -> Extract -> Layout 抽取布局 include标签

include 标签

include标签常用于将布局中的公共部分提取出来供其他layout共用,以实现布局模块化,也是平常我们设计布局时用的最多的

include 官方文档

include使用方法

1.定义要实现(抽取)的layout布局:

include_test.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:gravity="center_horizontal"
              android:orientation="vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="@string/textview"
        android:textSize="24sp"/>


    <EditText
        android:id="@+id/editText"
        android:hint="@string/divide"
        android:layout_width="300dp"
        android:layout_height="wrap_content"/>

</LinearLayout>

include_text_relative.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"
                android:gravity="center_horizontal"
    >

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="TextView_Relative"
        android:textSize="24sp"/>


    <EditText
        android:id="@+id/editText"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView"
        android:hint="@string/divide"/>

</RelativeLayout>

 

2.Activity的XML布局文件调用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 这样写就可以,加载的布局的子View,直接findViewByID就能找到-->
    <include layout="@layout/include_text"/>

    <!--如果有多个include,需要添加ID属性-->
    <include
        android:id="@+id/include_text1"
        layout="@layout/include_text"/>

    <!--这个layout用RelativeLayout 实现-->
    <!--如果要使用layout_margin这样的属性,要同时加上layout_w/h属性,不然没反应-->
    <include
        android:id="@+id/include_text2"
        layout="@layout/include_text_relative"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="50dp"/>


</LinearLayout>

 

3.Activity中调用include标签layout中的子View:


 private void initView() {

        //普通include标签用法,直接拿子View属性实现
        TextView textView = (TextView) findViewById(R.id.textView);
        textView.setText("不加ID实现的include标签");

        //多个include标签用法,添加ID,findViewByID找到layout,再找子控件
        View view_include = findViewById(R.id.include_text1);
        TextView view_include_textView = (TextView) view_include.findViewById(R.id.textView);
        view_include_textView.setText("加了ID实现的include标签");

    }

include使用注意

  • 一个xml布局文件有多个include标签需要设置ID,才能找到相应子View的控件,否则只能找到第一个include的layout布局,以及该布局的控件
  • include标签如果使用layout_xx属性,会覆盖被include的xml文件根节点对应的layout_xx属性,建议在include标签调用的布局设置好宽高位置,防止不必要的bug
  • include 添加id,会覆盖被include的xml文件根节点ID,这里建议include和被include覆盖的xml文件根节点设置同名的ID,不然有可能会报空指针异常
  • 如果要在include标签下使用RelativeLayout,如layout_margin等其他属性,记得要同时设置layout_width和layout_height,不然其它属性会没反应

merge 标签

merge标签主要用于辅助include标签,在使用include后可能导致布局嵌套过多,多余的layout节点或导致解析变慢。merge用于消除视图层次结构中的冗余视图,例如根布局是Linearlayout,那么我们又include一个LinerLayout布局就没意义了,反而会减慢UI加载速度

扫描二维码关注公众号,回复: 10659074 查看本文章

merge 官方文档

merge标签常用场景:

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

  2. 自定义View如果继承LinearLayout(ViewGroup),建议让自定义View的布局文件根布局设置成merge,这样能少一层结点。

merge使用方法

1.对于上面的情景1,在XML布局文件的根布局将FrameLayout直接改成merge即可

2.对于上面的情景2,因为为merge标签的设置的属性都不会生效,所以原来LinearLayout标签上的属性需要转移到java代码中设置。

<?xml version="1.0" encoding="utf-8"?>
<!-- 习惯性的标记一下,MergeLayout布局 android:orientation="vertical" -->
<merge xmlns:android="http://schemas.android.com/apk/res/android" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1.0"
        android:background="#000000"
        android:gravity="center"
        android:text="第一个TextView"
        android:textColor="#ffffff" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1.0"
        android:background="#ffffff"
        android:gravity="center"
        android:text="第一个TextView"
        android:textColor="#000000" />

</merge>
/**
 * 自定义的View,竖直方向的LinearLayout
 */
public class MergeLayout extends LinearLayout {

    public MergeLayout(Context context) {
        super(context);
        // 设置为数值方向的布局
        setOrientation(VERTICAL);
        LayoutInflater.from(context).inflate(R.layout.merge_activity, this, true);
    }
}

ViewStub 标签

ViewStub 标签最大的优点是当你需要时才会加载,使用它并不会影响UI初始化时的性能.各种不常用的布局像进度条、显示错误消息等可以使用ViewStub标签,以减少内存使用量,加快渲染速度

ViewStub 官方文档链接

ViewStub使用场景

通常用于不常使用的控件,比如:

  • 网络请求失败的提示
  • 列表为空的提示
  • 新内容、新功能的引导, 因为引导基本上只显示一次
  • 又或者我们写了一个通用的自定义 View. 但其中部分子 View 只在部分情况下才显示.

ViewStub使用方法

<?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">

        <include
            android:id="@+id/titleLayout"
            layout="@layout/title_back"
            android:visibility="gone" />

        <FrameLayout
            android:id="@+id/contentContainer"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/titleLayout" />

        <ViewStub
            android:id="@+id/view_stub_nodata"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            <!--android:inflateId:重写ViewStub的父布局控件的Id-->
            android:inflatedId="@+id/rl_net_error_root"  
            <!--android:layout:设置ViewStub被inflate的布局-->
            android:layout="@layout/common_stub_no_data" />

  

</RelativeLayout>

common_stub_net_error.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/rl_net_error_root"
                android:background="#F0F0F0"
                xmlns:android="http://schemas.android.com/apk/res/android">
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_centerInParent="true"
        >
        <ImageView
            android:id="@+id/txt_net_error"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/empty_pic_net"
            android:layout_centerInParent="true"
            android:gravity="center_horizontal"
            />
        <Button
            android:id="@+id/btn_net_refresh"
            android:layout_width="122dp"
            android:layout_height="35dp"
            android:background="@drawable/shape_round_blue"
            android:layout_marginTop="16dp"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:text="刷新"
            android:layout_gravity="center_horizontal"
            />
    </LinearLayout>
</RelativeLayout>
     
 

当您想要加载 ViewStub 指定的布局时,可通过调用 setVisibility(View.VISIBLE) 将其设为可见,或调用 inflate()。

viewStub.inflate()方法只可调用一次,多次调用会抛出异常(解决方法如下代码)。若只是进行展示/隐藏则直接调用setVisibility,无需使用inflate方法;若需要对替换后的视图进行操作则可使用inflate方法,其会返回替换后的视图。

    //第一种
    private View newLayout;
    private ViewStub viewStub;
    public void show(boolean show) {
        if(newLayout == null){
            viewStub = findViewById(R.id.view_stub_nodata);
            newLayout = viewStub.inflate();
        }
            viewStub.setVisibility(show ? View.VISIBLE : View.GONE);
    }
   //第二种
    private View newLayout;
    private ViewStub viewStub;
    public void show() {
        viewStub = (ViewStub) findViewById(R.id.view_stub_nodata);
        try {
            viewStub.inflate();
        } catch (IllegalStateException e) {
            viewStub.setVisibility(View.VISIBLE);
        }
    }

ViewStub使用注意

  • ViewStub标签不支持merge标签
  • ViewStub的inflate只能被调用一次,第二次调用会抛出异常,setVisibility可以被调用多次,但不建议这么做(ViewStub 调用过后,可能被GC掉,再调用setVisibility()会报异常)
  • 为ViewStub赋值的android:layout_XX属性会替换待加载布局文件的根节点对应的属性
发布了63 篇原创文章 · 获赞 1 · 访问量 2101

猜你喜欢

转载自blog.csdn.net/weixin_42046829/article/details/104606190