当前位置: 首页 > news >正文

【Android】布局优化:include、merge、ViewStub的使用及注意事项

【Android】布局优化:include、merge、ViewStub的使用及注意事项

在 Android 布局优化中,include、merge 和 ViewStub 是三种常用的布局标签。include 主要用于布局重用,merge 一般和 include 配合使用,它可以减少布局嵌套层级,而 ViewStub 则提供了按需加载的功能,当需要时才会将 ViewStub 中的布局加载到内存,提高了程序初始化效率,下面分别介绍它们的使用方法:

一、include

在 Android 开发中,<include> 标签用于实现布局复用。我们通常会将一些通用的界面元素单独抽取到一个独立的布局文件中,然后通过 <include> 标签在其他布局中进行引用。这样不仅方便对相同视图进行统一维护和修改,也有效提高了布局的重用性与开发效率。

举个栗子,以标题栏为例,抽取布局如下:

my_title_layout.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:background="#00BCD4"><ImageButtonandroid:id="@+id/back_btn"android:layout_width="48dp"android:layout_height="48dp"android:src="@drawable/ic_back"android:backgroundTint="#00FFFFFF"/><TextViewandroid:id="@+id/title_tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_marginStart="20dp"android:layout_toEndOf="@+id/back_btn"android:gravity="center"android:text="我的title"android:textSize="18sp" /></RelativeLayout>

使用也很简单,如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><include layout="@layout/my_title_layout"/></LinearLayout>

注意事项

include 使用有几点需要注意:

  1. 当同一个 XML 布局文件中包含多个 <include> 标签时,建议为每个 <include> 单独设置 id 属性。否则,在代码中通过 findViewById() 获取子视图时,只能找到第一个被引入的布局及其内部控件,后续的 <include> 所对应的视图将无法正确访问。
  2. 如果被引入的布局文件的根视图本身定义了 android:id,而 <include> 标签也设置了 android:id,则建议保持两者一致。否则在代码中通过 findViewById() 访问根视图时,可能会出现返回 null 的情况。
  3. <include> 标签中,我们可以重写被引入布局中的所有 layout 属性,但无法重写普通的非 layout 属性(如背景颜色、文字大小等)。需要特别注意的是,若要在 <include> 标签中对 layout 属性进行重写,必须同时显式指定 layout_widthlayout_height,否则所覆写的属性将不会生效。

二、merge

merge标签可用于减少视图层级来优化布局,可以配合include使用,如果include标签的父布局 和 include布局的根容器是相同类型的,那么根容器的可以使用merge代替。<include>标签存在着一个不好的地方,可能会导致产生多余的布局嵌套。举个栗子:

my_choice_layout.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="match_parent"android:orientation="vertical"><Buttonandroid:id="@+id/ok"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginEnd="40dp"android:layout_marginStart="40dp"android:text="确定"/><Buttonandroid:id="@+id/cancel"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginEnd="40dp"android:layout_marginStart="40dp"android:text="取消"/></LinearLayout>

这里定义了两个按钮,在布局中引用:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><include layout="@layout/my_title_layout"/><EditTextandroid:layout_width="match_parent"android:layout_height="wrap_content"android:hint="输入"android:layout_margin="40dp"/><include layout="@layout/my_choice_layout"/></LinearLayout>

运行结果如下:

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

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

修改my_choice_layout.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"><Buttonandroid:id="@+id/ok"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginEnd="40dp"android:layout_marginStart="40dp"android:text="确定"/><Buttonandroid:id="@+id/cancel"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginEnd="40dp"android:layout_marginStart="40dp"android:text="取消"/></merge>

此时布局结构如下:

可以看到,这里去除了多余的嵌套。

注意事项

  1. 如果一个布局文件的根容器是 FrameLayout,且没有设置 backgroundpadding 等属性,那么完全可以使用 <merge> 来替代它。因为 Activity 的默认 ContentView 外层本身就是一个 FrameLayout,此时再嵌套一层 FrameLayout 会造成多余的层级。使用 <merge> 可以让布局内容直接插入到父容器中,从而减少渲染层次,提升性能。
  2. 由于 <merge> 并非一个实际的 View 对象,因此在通过 LayoutInflater.inflate() 手动加载时必须为其指定父容器,并且第三个参数要传入 true,表示将子视图立即附加到父容器中。
  3. <merge> 只能作为布局文件的根节点使用,不能嵌套在其他布局中。如果它出现在非根层级位置,Android Studio 会直接报错或在运行时崩溃。此外,ViewStub 引用的布局文件中禁止使用 <merge> 作为根节点,因为 ViewStub 会通过 inflate() 动态创建视图,而 <merge> 无法独立生成视图对象,这会导致 InflateException 异常。
  4. 与普通布局不同,当使用 <include> 引入一个以 <merge> 为根的布局时,不能在 <include> 标签中重写布局属性(如 layout_widthlayout_height),因为 <merge> 没有自己的根容器,这些属性会被直接忽略。

三、ViewStub

在实际开发中,我们经常会遇到这样的情况:页面中存在一些在初始化阶段暂时不需要显示的布局。虽然可以通过将它们的可见性设置为 invisiblegone 来隐藏,但这些布局在界面加载时依然会被解析与创建,从而增加页面的初始化开销。为了解决这一问题,Android 提供了一个轻量级的解决方案 —— ViewStub。它是一个不可见、尺寸为 0 的占位视图,具备 懒加载(延迟加载) 的特性。ViewStub 虽然存在于视图层级结构中,但只有在调用 setVisibility()inflate() 方法时才会真正加载并替换成目标布局,因此不会影响页面的初始渲染性能。

举个栗子:

extra_layout.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="match_parent"android:orientation="vertical"><EditTextandroid:id="@+id/et_1"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginEnd="40dp"android:layout_marginStart="40dp"android:hint="学号"/><EditTextandroid:id="@+id/et_2"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginEnd="40dp"android:layout_marginStart="40dp"android:hint="班级"/></LinearLayout>

这里设置两个输入框,作为要延迟加载的布局。

布局中使用:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><include layout="@layout/my_title_layout"/><EditTextandroid:layout_width="match_parent"android:layout_height="wrap_content"android:hint="姓名"android:layout_marginTop="40dp"android:layout_marginStart="40dp"android:layout_marginEnd="40dp"/><Buttonandroid:id="@+id/btn_more"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="更多"android:layout_gravity="end"/><ViewStubandroid:id="@+id/view_stub"android:layout="@layout/extra_layout"android:layout_width="match_parent"android:layout_height="wrap_content" /><include layout="@layout/my_choice_layout"/></LinearLayout>

在代码中加载:

public class MainActivity extends AppCompatActivity {private EditText editText1;private EditText editText2;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_main);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});Button button = (Button) findViewById(R.id.btn_more);button.setOnClickListener(v -> {ViewStub viewStub = (ViewStub) findViewById(R.id.view_stub);if(viewStub != null) {View view = viewStub.inflate();editText1 = view.findViewById(R.id.et_1);editText1 = view.findViewById(R.id.et_2);}});}
}

运行程序,效果如下:

注意事项

  1. 由于 ViewStub 不是一个实际的视图容器,因此它在加载布局时不支持使用 <merge> 作为根布局。因此这有可能导致加载出来的布局存在着多余的嵌套结构。
  2. ViewStub 的懒加载机制决定了它在第一次调用 inflate() 或设置 setVisibility(View.VISIBLE) 后会被实际布局替换,并从视图树中移除。因此,同一个 ViewStub 不能被重复加载。如果第二次调用 inflate(),系统会抛出 IllegalStateException 异常。若需多次显示该布局,建议保存 inflate() 返回的视图引用,通过 setVisibility() 控制显示与隐藏。
  3. 虽然 ViewStub 自身不参与绘制,也几乎不占用空间,但它仍然是一个有效的视图占位符。因此,布局文件中若未显式声明 android:layout_widthandroid:layout_height,系统在解析时会抛出异常。
    视图树中移除。因此,同一个 ViewStub 不能被重复加载。如果第二次调用 inflate(),系统会抛出 IllegalStateException 异常。若需多次显示该布局,建议保存 inflate() 返回的视图引用,通过 setVisibility() 控制显示与隐藏。
  4. 虽然 ViewStub 自身不参与绘制,也几乎不占用空间,但它仍然是一个有效的视图占位符。因此,布局文件中若未显式声明 android:layout_widthandroid:layout_height,系统在解析时会抛出异常。
http://www.dtcms.com/a/503756.html

相关文章:

  • PHP 桌面端框架NativePHP for Desktop v2 发布!
  • 第7章 muduo编程示例(2)
  • 哪里有放网站的免费空间无锡市政务服务网站建设项目
  • 为什么Unity修改过物体,物体的位移和旋转还是会被改变
  • Dify 平台从 x86_64 迁移至 ARM64 架构完整指南
  • 站嗨建站适合小白的室内设计软件
  • 基于车速预测的燃料电池混合动力汽车能量管理策略:一种自适应ECMS方法及其Python实现
  • [嵌入式系统-149]:新能源汽车的三电系统以及其功能、硬件架构、嵌入式操作系统
  • 在iStoreOS系统中安装Docker:从基础到高级应用的完整指南
  • 建设银行 网站查询密码杭州清风室内设计学校
  • 专业的个人网站建设商务网站建设与维护 ppt
  • 《Python红队攻防零基础脚本编写:入门篇(二)》
  • 装修设计网站免费婚庆网站建设策划案费用预算
  • 限制GIT提交大文件
  • STM32F103C8T6_IIC协议详解
  • 几个好用的在线网站南京网络营销
  • 乐学LangChain(1):总体架构和核心组件
  • CAN 总线物理层介绍
  • yolo介绍
  • 中国建站公司网站电线电话图怎么做
  • 新华路街道网站建设企业为什么要建设电子商务网站
  • UVa 1630 Folding
  • 基于AT89C52的智能温控风扇设计
  • 32HAL——IIC温度传感器AHT20
  • 站群wordpress宣武深圳网站建设公司
  • 构建自主AI的挑战与对策:稳定性、成本与伦理安全
  • 汽车服务站建站流程网页网站原型图占位符怎么做
  • 高效解决重装后常见的7类系统适配问题
  • 如何写好代码
  • PS2020使用教程|绘制文明行车宣传画