概述

Android为XML布局提供了多种类型的组件标签供使用,其中include、merge、ViewStub不常用,这三个标签用于布局优化,本文对这三个标签进行初略讲解。

include

include是包含的意思,顾名思义就是布局重用的意思,也就是一块布局写好了之后,可以在其他布局中重复使用。实际开发中,使用include标签地方不少,包括标题栏、控制面板、进度条、统一的按钮文字等等,凡是共用的布局都可以使用include,大大减少了程序员的工作量,提高了开发效率。
以标题栏为例阐述include的使用。

  1. 要自定义标题栏titlebar,需要将app主题设为NoActionBar。
    <style name="Theme.Content" parent="Theme.MaterialComponents.DayNight.NoActionBar">
  1. 创建空白布局。
<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"tools:context=".MainActivity"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="Hello World!"android:textColor="@color/black"android:textSize="48dp" />
</LinearLayout>

因为取消了系统默认的标题栏,所以预览图中标题部分是没有的。

3. 自定义标题栏。

<?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="52dp"android:background="#00BCD4"><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_centerVertical="true"android:src="@drawable/ic_baseline_keyboard_arrow_left_24" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="这是标题"android:textColor="@color/white"android:textSize="28dp" />
</RelativeLayout>

自定义标题栏中包含返回按钮和标题。
4. include标题栏

<?xml version="1.0" encoding="utf-8"?>
<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"tools:context=".MainActivity"><include layout="@layout/titlebar" /><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="Hello World!"android:textColor="@color/black"android:textSize="48dp" />
</LinearLayout>


使用include标签,需要注意一下几点。

  • include标签不能作为根布局元素
            } else if (TAG_INCLUDE.equals(name)) {if (parser.getDepth() == 0) {throw new InflateException("<include /> cannot be the root element");}parseInclude(parser, context, parent, attrs);
  • include标签内不可以再添加其他元素。
    <includelayout="@layout/titlebar" ><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="Hello World!"android:textColor="@color/black"android:textSize="48dp" /></include>

像上面的写法虽然不会报错,但是TextView却无法展示出来。

  • include只能用在ViewGroup内
        if (!(parent instanceof ViewGroup)) {throw new InflateException("<include /> can only be used inside of a ViewGroup");}
  • 若include添加了一些属性,则会以添加后的为准,且include里面包含的元素也会以添加后的属性为准。
       // Apply a theme wrapper, if requested. This is sort of a weird// edge case, since developers think the <include> overwrites// values in the AttributeSet of the included View. So, if the// included View has a theme attribute, we'll need to ignore it.final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);final int themeResId = ta.getResourceId(0, 0);final boolean hasThemeOverride = themeResId != 0;if (hasThemeOverride) {context = new ContextThemeWrapper(context, themeResId);}ta.recycle();
    <includeandroid:layout_width="100dp"android:layout_height="wrap_content"layout="@layout/titlebar" ></include>

此时页面效果就会发生改变。

  • 若include中有设置了id,include包含的视图中也设置了id,则不能通过include自己的id获取include里面的组件
    <includeandroid:id="@+id/include_title"layout="@layout/titlebar" ></include>

titlebar.xml中布局如下。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/rl_title"android:layout_width="match_parent"android:layout_height="52dp"android:background="#00BCD4"><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_centerVertical="true"android:src="@drawable/ic_baseline_keyboard_arrow_left_24" /><TextViewandroid:id="@+id/tv_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="这是标题"android:textColor="@color/white"android:textSize="28dp" /></RelativeLayout>

此时通过rl_title寻找tv_title会出现空指针。

        RelativeLayout titleBar = findViewById(R.id.rl_title);TextView title = titleBar.findViewById(R.id.tv_title);

结果会出现空指针异常。

     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.widget.RelativeLayout.findViewById(int)' on a null object referenceat com.example.content.MainActivity.onCreate(MainActivity.java:22)

include标签复用其他布局时,可以视为在布局中又写了一遍,所以获取复用布局里面的元素直接使用findViewById即可。

TextView title = findViewById(R.id.tv_title);

无需借助复用布局的再一次findViewById。

merge

merge翻译过来的意思是合并、融入、融合、兼并的意思,merge标签也就是起这样的作用,用于减少布局层级,防止页面过渡绘制,减少绘制时间,提高性能。

不使用merge

普通布局。

<?xml version="1.0" encoding="utf-8"?>
<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"tools:context=".MainActivity"><include layout="@layout/titlebar"/><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="Hello World!"android:textColor="@color/black"android:textSize="48dp" /><include layout="@layout/normal_item"/></LinearLayout>

其中normal_item没有使用merge。

<?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"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="24dp"android:textColor="@color/black"android:text="第一个普通测试文本"/><TextViewandroid:layout_marginTop="50dp"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="24dp"android:textColor="@color/black"android:text="第二个普通测试文本"/></LinearLayout>

结果布局中的树如下:

一共有两级组件。

使用merge

使用merge就是将上诉normal_item中LinearLayout改成merge。

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="24dp"android:textColor="@color/black"android:text="第一个merge测试文本"/><TextViewandroid:layout_marginTop="50dp"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="24dp"android:textColor="@color/black"android:text="第二个merge测试文本"/></merge>

组件树如下:

结果发现使用了merge后,merge里面的组件和父容器里面的组件是同一层级,而没有使用merge是两级,所以merge主要功能是用来减少布局层级。
使用merge也需要注意几点。

  • merge是一个标签而不是一个view,所以在merge上不需要设置任何属性
<merge xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent">

像上面的写法其实是没有任何效果的,merge里面的组件是根据使用merge的父容器来的。

  • 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"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="24dp"android:textColor="@color/black"android:text="第一个merge测试文本"/><TextViewandroid:layout_marginTop="50dp"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="24dp"android:textColor="@color/black"android:text="第二个merge测试文本"/></merge>

否则代码编译的时候会报错。

            } else if (TAG_MERGE.equals(name)) {throw new InflateException("<merge /> must be the root element");} else {
  • merge必须要有父容器
                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);

ViewStub

A ViewStub is an invisible, zero-sized View that can be used to lazily inflate layout resources at runtime. When a ViewStub is made visible, or when inflate() is invoked, the layout resource is inflated. The ViewStub then replaces itself in its parent with the inflated View or Views. Therefore, the ViewStub exists in the view hierarchy until setVisibility(int) or inflate() is invoked. The inflated View is added to the ViewStub’s parent with the ViewStub’s layout parameters. Similarly, you can define/override the inflate View’s id by using the ViewStub’s inflatedId property.

简单来讲,ViewStub默认是一个不可见,且宽高都为0的,只有在运行时才会加载(懒加载)的组件。

    public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context);final TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.ViewStub, defStyleAttr, defStyleRes);saveAttributeDataForStyleable(context, R.styleable.ViewStub, attrs, a, defStyleAttr,defStyleRes);mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);a.recycle();setVisibility(GONE);setWillNotDraw(true);}...@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(0, 0);}

ViewStub默认显示为GONE,宽高都为0,此时也不会绘制。需要显示的时候,需要调用setVisibility()或者Inflate()才可以加载布局。

使用
    <ViewStubandroid:id="@+id/vs_test"android:layout_width="200dp"android:layout_height="200dp"android:inflatedId="@+id/vs_inflate"android:layout="@layout/viewstub_item" />

其中viewstub_item:

<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/lv_test"android:layout_width="match_parent"android:layout_height="match_parent">
</ListView>

通过inflatedId就可以获取viewstub_item里面的ListView 。

        ViewStub viewStub = findViewById(R.id.vs_test);// 加载viewStub.setVisibility(View.VISIBLE);// 通过inflatedId获取viewstub_item里面的listViewListView listView = findViewById(R.id.vs_inflate);

当inflatedId不为NO_ID,也就是设置了id的时候,此时ViewStub的inflatedId就成为了根元素的id,此时可以直接通过findViewById获取ViewStub 里面的元素。
如果没有设置inflatedId时,可以直接通过findViewById(R.id.lv_test)获取ViewStub里面的ListView。也可以通过inflate()直接获取ViewStub里面的ListView。

        ViewStub viewStub = findViewById(R.id.vs_test);// 获取ListViewListView inflate = (ListView) viewStub.inflate();if (inflate == null) {// 未加载} else {// 已加载}

注意:

  • 可以通过判断ViewStub的可见性(Visibility)判断是否已加载。
  • 如果通过inflate(),则需要通过对inflate获取对象进行判空来判断是否加载。
  • 如果设置了inflatedId,则需要通过inflatedId查找目标父容器。

Android随笔-include、merge、ViewStub相关推荐

  1. android layout include merge,Android 布局优化之include与merge

    Android 官方提供了三个用来优化布局的标签,分别是include.merge与ViewStub,其中ViewStub是动态加载视图到内存,大家可以查阅:Android UI布局优化之ViewSt ...

  2. [转载]Android Layout标签之-viewStub,requestFocus,merge,include

    定义Android Layout(XML)时,有四个比较特别的标签是非常重要的,其中有三个是与资源复用有关,分别是<viewStub/>, <requestFocus />, ...

  3. Android性能优化:布局优化 详细解析(含include、ViewStub、merge讲解 )

    1. 影响的性能 布局性能的好坏 主要影响 :Android应用中的页面显示速度 2. 如何影响性能 布局影响Android性能的实质:页面的测量 & 绘制时间 1个页面通过递归 完成测量 & ...

  4. Android Layout标签之-viewStub,requestFocus,merge,include

    定义Android Layout(XML)时,有四个比较特别的标签是非常重要的,其中有三个是与资源复用有关,分别是<viewStub/>, <requestFocus />, ...

  5. Android布局优化之ViewStub、include、merge使用与源码分析

    在开发中UI布局是我们都会遇到的问题,随着UI越来越多,布局的重复性.复杂度也会随之增长.Android官方给了几个优化的方法,但是网络上的资料基本上都是对官方资料的翻译,这些资料都特别的简单,经常会 ...

  6. include、ViewStub、merge优化布局标签

    前言 在写Android的xml布局时,用好 include.ViewStub.merge这三个标签,可以是我们的xml更加简洁.高效. include 按照官方的意思,include就是为了解决重复 ...

  7. Android UI性能优化——ViewStub和Merge的使用

    ViewStub的使用 简介 ViewStub 是一种没有任何维度的轻量型视图,它不会绘制任何内容或参与布局. ViewStub是一种没有大小,不占用布局的View. 直到当调用 inflate() ...

  8. Android实战技巧:ViewStub的应用

    在开发应用程序的时候,经常会遇到这样的情况,会在运行时动态根据条件来决定显示哪个View或某个布局.那么最通常的想法就是把可能用到的View都写在上面,先把它们的可见性都设为View.GONE,然后在 ...

  9. android模拟按键方法,Android随笔之——模拟按键操作的几种方式

    前几天转过一篇Android上模拟按键操作.触屏事件的博客,昨天又去找了百度.谷歌了一下,写了一点简单的测试代码,留待不时之需.有需要看之前转载的那篇博客的请看这里→_→转:Android随笔之--使 ...

  10. include和ViewStub的区别

    最近优化项目内存的时候了解到ViewStub,include和ViewStub都是引用布局的标签,但是它们是有区别的: include和ViewStub的区别 1.include是可以在布局中引入另一 ...

最新文章

  1. 号称3个月发布最强量子计算机,卖口罩的霍尼韦尔凭什么?
  2. 1.2.2 OSI参考模型
  3. for循环中new的对象什么时候被回收_你真的了解JS里的 new 吗?
  4. Hive引擎改为Tez笔记
  5. 现代操作系统原理与实践04:实验1:机器启动
  6. 行为型设计模式(1)—— 责任链模式(Chain of Responsibility Pattern)
  7. Mysql自增id用完怎么办?
  8. 基本排序算法之1——希尔排序shellsort
  9. android APN的打开与关闭
  10. 左神数据结构与算法(基础提升)——01
  11. spark sample采样
  12. 什么是软件,软件有哪些特性?
  13. kettle 6.1.0.1 mysql_kettle连接数据库报错:Error occured while trying to connect
  14. 发烧怎么办?按这5个穴位
  15. MySQL如何查询表中重复的数据
  16. 海思 YOLOv5 pytorch 转 onnx 转 Caffe 再转 wk 的转化详解
  17. 耐高压达林顿输出光耦(TLP127,TLP187,TLP627)功能介绍及应用实例
  18. 小猫统计——快速数据统计专家介绍
  19. 教授专栏54 | 香港科大商学院绿色金融研究小组:借鉴外地经验,培育绿色金融人才...
  20. GD32F4xx MCU 驱动mcp2515扩展CAN接口

热门文章

  1. 怎么用硕鼠下载优酷专辑
  2. mysql考勤查询,查询mysql中的考勤表
  3. 【学习笔记】概率论与数理统计 - 陈希孺--第一章.事件的概率
  4. pl/sql 存储过程实例
  5. 计算机组成原理(第3版)思考题与习题答案----唐朔风 编著
  6. 全国一级计算机考什么,全国计算机等级考试一级考什么
  7. Qt 在VS2005安装过程
  8. windows命令大全
  9. JAVA 工厂模式计算器
  10. teechart mysql_TeeChart的X轴为时间,多个Y轴的显示