Android 精华Android技术知识首页投稿(暂停使用,暂停投稿)

Hacks布局篇-Hack4 使用ViewStub实现延迟加载

2016-05-07  本文已影响500人  diygreen
ViewStub

作者:李旺成

时间:2016年5月7日


该 Hack 将介绍如何使用 ViewStub 实现视图的延迟加载(懒加载)。

ViewStub 简介

在 Android 开发中,经常会遇到这样的情况,在程序运行过程中动态的根据当前条件来决定是否显示某个控件或布局,一般大家都会使用 View 的 setVisibility() 方法来控制。这里介绍另外一种方式,那就是使用 ViewStub 来方便的完成这一功能。

先看看官方文档对 ViewStub 的介绍:

ViewStub文档

简单翻译下:
VeiwStub 是一种不可视并且大小为 0 的视图,可以延迟到运行时填充(inflate)布局资源。当 ViewStub 设置为可视或者 inflate() 方法被调用后,就会填充布局资源,然后 ViewStub 便会被填充的视图替代。

一句话介绍:

ViewStub 最大的优点
是当你需要时才会加载,使用它并不会影响 UI 初始化时的性能(可以说提高了初始化时的性能)。各种不常用的布局,比如:进度条、显示错误消息等都可以使用 ViewStub 来动态加载,以减少内存使用,加快渲染速度(懒加载 —— 用到的时候才加载)。

说明:在动态加载布局时,使用 ViewStub 的性能要比使用设置 View 的可见性高。因为虽然把 View 的初始可见 View.GONE,使其不可见,但是在 Inflate 布局的时候 View 仍然会被 Inflate,也就是说仍然会创建对象,会被实例化,会被设置属性。也就是说,会耗费内存等资源。

ViewStub 简单使用

先看效果:

ViewStub实现懒加载

来看看怎么用吧:
1、在 Layout 中使用

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    >
    ...
    <ViewStub
        android:id="@+id/vs_test2"
        android:inflatedId="@+id/vs_header"
        android:layout="@layout/header_withmerge"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"/>
    ...
    <ViewStub
        android:id="@+id/vs_test"
        android:inflatedId="@+id/vs_footer"
        android:layout="@layout/footer_app_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="20dp"/>
</RelativeLayout>

inflatedId 属性:该属性的作用是重新定义引用的 layout id,其属性值是调用 ViewStub 的 inflate() 方法时返回的 View 的 ID
layout 属性:该属性用于指定 inflate 时要填充的布局(与 include 的 layout 用法完全一样)
layout_marginXxx 属性:这些属性设置在 layout 指定的子布局中是没有作用的,需要放在它的 ViewStub 上面才会起作用。ViewStub 的属性在 inflate 后会都传给相应的布局

**2、ViewStub.inflate() 方法 **
看一下方法签名:

public View inflate();
inflate 方法

该方法返回一个 View,就是 layout 属性设置的布局。所以,一般都是这样用:

TextView fooder = (TextView) mTestVS.inflate();
fooder.setText("我是ViewStub Inflate的页脚");

**3、ViewStub.setVisibility() 方法 **
ViewStub 重写了 View 的 setVisibility() 方法,先看看源码:

/**
 * When visibility is set to {@link #VISIBLE} or {@link #INVISIBLE},
 * {@link #inflate()} is invoked and this StubbedView is replaced in its parent
 * by the inflated layout resource. After that calls to this function are passed
 * through to the inflated view.
 *
 * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
 *
 * @see #inflate() 
 */
@Override
@android.view.RemotableViewMethod
public void setVisibility(int visibility) {
    if (mInflatedViewRef != null) {
        View view = mInflatedViewRef.get();
        if (view != null) {
            view.setVisibility(visibility);
        } else {
            throw new IllegalStateException("setVisibility called on un-referenced view");
        }
    } else {
        super.setVisibility(visibility);
        if (visibility == VISIBLE || visibility == INVISIBLE) {
            inflate();
        }
    }
}

从源码中可以看出:ViewStub 的 setVisibility 成 View.VISIBLE 或 INVISIBLE 如果是首次使用,都会自动 inflate (调用 inflate() 方法)其指向的布局文件,并替换 ViewStub 本身,再次使用则是相当于对其指向的布局文件设置可见性。

4、小结
Layout 中:
把 ViewStub 当作占位符,设置 layout 属性,指向目的布局

Java 代码中:
findViewById() --> inflate() / setVisibility()

注意事项

使用 ViewStub 的时候有下面几点需要注意:

ViewStub Inflate <merge /> 标签布局 ViewStub多次Inflate

也就是说:ViewStub 对象只可以 Inflate 一次,之后 ViewStub 对象会被置为空。按句话说,某个被 ViewStub 对象指定的布局被 Inflate 一次之后,就不可以再次通过 ViewStub 对象来控制它了。(参考自:使用惰性控件ViewStub实现布局动态加载

项目地址

AndroidHacks合集
布局篇

示例用到代码见:
Hack4Activity.java
activity_hack4.xml

参考

使用惰性控件ViewStub实现布局动态加载
Android性能优化之一:ViewStub
Android实战技巧:ViewStub的应用

上一篇 下一篇

猜你喜欢

热点阅读