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 来访问

相关推荐
齊家治國平天下27 分钟前
AAOS 电源管理深度解析:休眠/唤醒/功耗优化
android·车载系统·aaos·aosp·电源管理·休眠唤醒·carpowermanager
Xzaveir1 小时前
企业号码展示不可观测怎么办:状态机、拨测事件与异常回放
android·人工智能
2501_916007471 小时前
iOS和macOS应用程序性能分析和优化工具使用综合指南
android·macos·ios·小程序·uni-app·iphone·webview
黄林晴2 小时前
Nearby Connections 重大变更:不再自动开启 Wi-Fi / 蓝牙,适配指南
android
恋猫de小郭2 小时前
Flutter 全新真 3D 实现,用 flutter_scene 能开发一个「我的世界」
android·前端·flutter
HLC++15 小时前
Linux的进程间通信
android·linux·服务器
爱笑鱼18 小时前
Binder(二):AIDL 生成的 Proxy、Stub 和 Parcel 到底在做什么?
android
爱笑鱼19 小时前
Binder(一):一次方法调用,究竟怎样跨进程执行?
android
壮哥_icon20 小时前
【Android 系统开发】使用 BAT 脚本高效自动化管理 /system/priv-app/ 系统应用(安装与卸载)
android·运维·自动化
用户693717500138421 小时前
Claude Code终端日志Token占用实测:一个过滤器砍掉60%-90%
android·前端·后端