ViewStub 讲解

一、ViewStub 是什么

ViewStub 是 Android 提供的一个轻量级、不可见且不占布局空间 的视图控件,它本身不绘制任何内容,也不参与布局测量。它的核心作用是作为"占位符",在需要时才加载指定的布局文件,从而实现延迟加载(Lazy Loading)。

主要优势:

  • 提升启动速度:避免一次性加载所有复杂布局,加快页面渲染
  • 节省内存:只有在真正需要时才会 inflate 布局,减少资源占用

二、工作原理

  • 页面初始化时,只有 ViewStub 这个轻量级占位符被加载(几乎不耗资源)
  • 当用户触发某个操作(如点击按钮)时,才调用 inflate()setVisibility(VISIBLE)
  • 此时,ViewStub 会被它引用的真实布局替换掉,并从视图树中移除自身

注意ViewStub 一旦被 inflate 就无法再次调用,因为它在视图树中已经被替换了,重复调用会抛出异常。


三、如何使用

步骤 1:定义要延迟加载的布局

例如,创建一个 layout/loading_layout.xml 文件:

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

    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="加载中..."
        android:layout_gravity="center" />
</LinearLayout>

步骤 2:在主布局中使用 ViewStub

xml 复制代码
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 主内容 -->
    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- ViewStub 占位符 -->
    <ViewStub
        android:id="@+id/stub_loading"
        android:inflatedId="@+id/panel_loading"
        android:layout="@layout/loading_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</FrameLayout>

关键属性说明:

  • android:idViewStub 自身的 ID,在 inflate 前用于查找它
  • android:inflatedId:替换后的布局根视图的 ID,inflate 后可通过此 ID 访问
  • android:layout:指向要延迟加载的布局文件

步骤 3:在代码中触发加载

方法一:调用 inflate()(推荐,可直接获取加载后的视图)

kotlin 复制代码
val viewStub = findViewById<ViewStub>(R.id.stub_loading)
val inflatedView = viewStub.inflate()  // 返回加载后的布局根视图

// 可以直接操作加载后的视图
val progressBar = inflatedView.findViewById<ProgressBar>(...)
java 复制代码
ViewStub viewStub = findViewById(R.id.stub_loading);
View inflatedView = viewStub.inflate();  // 返回加载后的布局根视图

方法二:设置 visibility

kotlin 复制代码
findViewById<View>(R.id.stub_loading).visibility = View.VISIBLE
java 复制代码
findViewById(R.id.stub_loading).setVisibility(View.VISIBLE);

两种方式效果相同,inflate() 方法会直接返回加载后的 View 对象,省去再调用一次 findViewById() 的麻烦。

步骤 4:监听加载完成(可选)

kotlin 复制代码
viewStub.setOnInflateListener { stub, inflated ->
    // 布局加载完成后的回调
    // 可以在这里进行额外的初始化操作
    val textView = inflated.findViewById<TextView>(R.id.text_view)
    textView.text = "动态设置的内容"
}
java 复制代码
viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
    @Override
    public void onInflate(ViewStub stub, View inflated) {
        // 布局加载完成后的回调
        TextView textView = inflated.findViewById(R.id.text_view);
        textView.setText("动态设置的内容");
    }
});

四、常见应用场景

场景 说明
列表空视图 当 ListView/RecyclerView 数据为空时才显示"暂无数据"提示,大部分情况下不需要加载
网络错误提示 仅在网络请求失败时才显示重试按钮或错误信息
高级设置面板 用户点击"更多设置"时才加载复杂的配置界面
引导/教程视图 首次打开 App 时才显示引导页,后续启动无需加载
进度条/加载动画 仅在执行耗时操作时才显示 loading 界面

五、重要注意事项

  1. 只能 inflate 一次ViewStub 被替换后就不存在了,多次调用 inflate() 会抛出异常

  2. 不支持 <merge> 标签 :被引用的布局文件中不能使用 <merge> 作为根标签

  3. 布局参数规则 :inflate 后,布局的宽高参数以 ViewStub 中定义的为准

  4. ID 有效期android:id 仅在 inflate 前有效,inflate 后应使用 android:inflatedId 来访问

相关推荐
故渊at1 小时前
第六板块:Android 安全与权限体系 | 第二十篇:应用签名、权限机制与 PackageManagerService 的安全校验
android·安全·权限体系·应用签名
朝星1 小时前
Android开发[11]:启动优化
android·kotlin
AI玫瑰助手1 小时前
Python函数:函数的文档字符串(docstring)编写
android·java·python
JohnnyDeng941 小时前
【Android】Android渲染机制:Choreographer与VSYNC深度解析
android·性能优化·kotlin·jetpack
恋猫de小郭1 小时前
Flutter 又为 AI 时代添砖加瓦:全新 ComponentLibrary 提议
android·前端·flutter
Mr -老鬼2 小时前
EasyClick 入门指南:Shell 命令与 ADB 完全指南
android·adb·自动化·shell·easyclick·易点云测
故渊at2 小时前
第五板块:Android 系统服务与电源管理 | 第十七篇:Power Manager Service 与 WakeLock 机制
android·pms·系统服务·电源管理·休眠唤醒
故渊at2 小时前
第七板块:Android 存储体系与文件系统 | 第二十一篇:Vold 与 FUSE 存储架构
android·架构·文件系统·fuse·vold·存储体系