按需加载视图

有时您的布局需要使用一些很少用到的复杂视图。无论是条目详情、进度指示器还是撤消消息,您都可以通过仅在需要时才加载这些视图来减少内存使用并加快渲染速度。

当应用在将来需要用到复杂视图时,您可以为这些复杂且很少使用的视图定义一个 ViewStub,从而推迟资源的加载。

定义 ViewStub

ViewStub 是一个没有尺寸的轻量级视图,它不执行任何绘制操作,也不参与布局。因此,它在视图层次结构中进行加载和驻留所消耗的资源极少。每个 ViewStub 都包含一个 android:layout 属性,用于指定要加载的布局。

假设您有一个希望在应用用户旅程后期才加载的布局:

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

    <ImageView
        android:src="@drawable/logo"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</FrameLayout>

您可以使用以下 ViewStub 来推迟加载。要让它显示或加载任何内容,必须使其显示所引用的布局。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ViewStub
    android:id="@+id/stub_import"
    android:inflatedId="@+id/panel_import"
    android:layout="@layout/heavy_layout_we_want_to_postpone"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom" />
</FrameLayout>

加载 ViewStub 布局

上一节中的代码片段产生的结果类似于图 1:

An image of a empty screen
图 1. 屏幕的初始状态:ViewStub 隐藏了重量级布局。

当您想要加载由 ViewStub 指定的布局时,可以通过调用 setVisibility(View.VISIBLE) 将其设置为可见,或者调用 inflate()

以下代码片段模拟了推迟加载的过程。屏幕在 ActivityonCreate() 中正常加载,随后显示 heavy_layout_we_want_to_postpone 布局:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.activity_old_xml)

  Handler(Looper.getMainLooper())
      .postDelayed({
          findViewById<View>(R.id.stub_import).visibility = View.VISIBLE
          
          // Or val importPanel: View = findViewById<ViewStub>(R.id.stub_import).inflate()
      }, 2000)
}

Java

@Override
void onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_old_xml);

  Handler(Looper.getMainLooper())
      .postDelayed({
          findViewById<View>(R.id.stub_import).visibility = View.VISIBLE
          
          // Or val importPanel: View = findViewById<ViewStub>(R.id.stub_import).inflate()
      }, 2000);
}
图 2. 重量级布局已可见。

一旦变得可见或已加载,ViewStub 元素将不再是视图层次结构的一部分。它会被加载的布局所取代,而该布局根视图的 ID 由 ViewStubandroid:inflatedId 属性指定。为 ViewStub 指定的 android:id 仅在 ViewStub 布局可见或已加载之前有效。

有关此主题的更多信息,请参阅博文 Optimize with stubs(使用存根进行优化)。