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:id:ViewStub 自身的 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 来访问

相关推荐
好的佩奇7 小时前
蓝牙之通信架构
android·flutter·蓝牙
FlightYe8 小时前
视界原理之3D视频(四):裸眼、眼镜和头显
android·linux·c++·3d·ffmpeg·音视频
诸神黄昏EX9 小时前
Android fw BatteryService
android
alexhilton9 小时前
提升 Android Compose 页面的流畅度
android·kotlin·android jetpack
撩得Android一次心动10 小时前
Android中Gradle网络下载超时、更改存放位置(解决方案)
android·gradle
HQL_seven12 小时前
【Activity】Android Activity详解
android
AI2中文网12 小时前
安卓 15/16 手机软键盘遮挡输入框、布局顶不上去?已解决:重新编译即可
android·智能手机
字节暗面12 小时前
Android APP 加固横评记录:爱加密、梆梆、网易易盾与腾讯 ACE
android·逆向工程·加固
落魄Android在线炒饭12 小时前
Android 实战问题反推 OpenGL 概念(上)
android·opengl
booboosui12 小时前
DeepSeek 的 Worker 调得动 GitHub,调不动微信:三类 Worker 的实现差别
android