这个问题也是头条面试官问的,本身没什么难度,但以前确实没仔细研究过。

1、使用介绍

ViewStub是一种不可见的尺寸为0的View,用来实现布局资源的懒加载。当ViewStub被设置为用户可见或其  inflate() 被调用时,实际的布局资源才会被加载。这时ViewStub在View树中的位置会被新加载的View取代,并且新加载的View会继承ViewStub所拥有的布局参数。而且我们还可以通过ViewStub的 inflatedId 属性定义新加载View的控件ID。

<ViewStub
android:id="@+id/stub"
android:inflatedId="@+id/subTree"
android:layout="@layout/mySubTree"
android:layout_width="120dip"
android:layout_height="40dip" />

此时ViewStub可以通过“stub”这个ID获取到,在“mySubTree”指向的布局被加载之后,ViewStub会被从View树中移出,“myStubTree”指向的布局便可以通过“subTree”这个ID获取到。新加载的布局会继承ViewStub的布局参数,在这里就是宽120dip,高40dip。利用StubView加载布局的方式如下:

ViewStub stub = findViewById(R.id.stub);
View inflated = stub.inflate();

当 inflate() 被调用以后,ViewStub被新加载的View取代。通过这种方式应用可以直接获取到新加载的View而无需再次调用  findViewById() 。

翻译的可能不是太准确,有兴趣的朋友可以看原文:StubView

2、源码分析

显然,我们需要关注的函数是ViewStub的 inflate() 。这个函数在使用的时候有一个需要注意的地方就是它只能被调用一次,至于为什么可以在源码中找到答案。

public View inflate() {
// 获取父View。
final ViewParent viewParent = getParent();
// 如果父View为null或者父View不是ViewGroup的子类则抛出异常。
if (viewParent != null && viewParent instanceof ViewGroup) {
// mLayoutResource即为StubView的layout属性的值,它代表了
// 需要被加载的布局ID。显然它不能为0。
if (mLayoutResource != 0) {
final ViewGroup parent = (ViewGroup) viewParent;
final LayoutInflater factory;
if (mInflater != null) {
factory = mInflater;
} else {
factory = LayoutInflater.from(mContext);
}
// 加载布局并实例化。
final View view = factory.inflate(mLayoutResource, parent,
false);
// 此处StubView将自己的inflatedId属性的值设置给了新加载的View。
if (mInflatedId != NO_ID) {
view.setId(mInflatedId);
}
// 紧接着StubView将自己从父View中移出。
final int index = parent.indexOfChild(this);
parent.removeViewInLayout(this);
// 将自己的LayoutParams设置给新加载的View,这就是为什么新加载的View会
// 继承ViewStub的布局参数。
final ViewGroup.LayoutParams layoutParams = getLayoutParams();
// 将新加载的View添加到父View中,而且是自己原来的位置。
if (layoutParams != null) {
parent.addView(view, index, layoutParams);
} else {
parent.addView(view, index);
}
// 保存新加载的View的弱引用。
mInflatedViewRef = new WeakReference<View>(view); if (mInflateListener != null) {
mInflateListener.onInflate(this, view);
} return view;
} else {
throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
}
} else {
throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
}
}

在函数的第26行可以看到,ViewStub将自己从父View中移除,所以如果再次调用 inflate() ,第5行的判断不成立就会抛出异常。在第37行,ViewStub保存了新加载的View的弱引用,为什么还要保存这个引用呢?这是因为在调用了 inflate() 之后,仍可以使用StubView的 setVisibility() 来设置新加载View的可见性。

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();
}
}
}

如果之前已将调用了 inflate() 那么 mInflatedViewRef 肯定不为 null ,否则如果参数为 VISIBLE 或 INVISIBLE 的话, inflate() 函数就会被调用。

3、总结

ViewStub如何提高我们的加载性能:

在它的初始化函数中,使用了 setVisibility(GONE) ,我们知道,可见性为 GONE 的View是不会被绘制且不占用空间的。

与直接将目标控件的可见性设置为 GONE 相比它的优势:

即使将可见性设置为 GONE ,在加载布局时仍需要对控件进行初始化等操作。这时ViewStub则显得十分轻量。

利用ViewStub实现布局懒惰加载的更多相关文章

  1. 利用青瓷布局自定义加载的场景,而不是自己改写qici-loading

    加载界面如果全部通过自己手动布局不仅不美观,还很难控制.借用原生的场景切换加载效果,来实现我们游戏的加载效果. 没有做加载修改的原来的加载顺序:   黑乎乎界面->(游戏定制的加载)你的第一个场 ...

  2. 利用简洁的图片预加载组件提升h5移动页面的用户体验

    在做h5移动页面,相信大家一定碰到过页面已经打开,但是里面的图片还未加载出来的情况,这种问题虽然不影响页面的功能,但是不利于用户体验.抛开网速的原因,解决这个问题有多方面的思路:最基本的,要从http ...

  3. Android 实现布局动态加载

    Android 动态加载布局 通过使用LayoutInflater 每次点击按钮时候去读取布局文件,然后找到布局文件里面的各个VIEW 操作完VIEW 后加载进我们setContentView 方面里 ...

  4. MVC学习七:Razor布局之加载分部视图【PartialView】

    Partial View 顾名思义就是Html代码片段,应用于此HTML代码多次被页面加载时使用.(类似于WebForm程序中的用户控件) 注:PartialView和正常的View页面在访问时没有任 ...

  5. C# winform利用反射和自定义特性加载功能模块(插件式开发)

    由于在实际的工作中, 碰见这样的一个问题: 一个软件, 销售给A客户 他需要所有功能, 但是销售给B客户, 他只需要其中的一部分, 1.如果我们在实际的开发过程中, 没有把一些功能模块区分开来的话, ...

  6. 利用Navigation Timing测量页面加载时间

    最近在看一本名为<web性能实践日志>的书籍,其中第十三章"网络计时"中介绍了一种比较新的计算页面各部分加载时间方法,这也是W3C Web性能工作小组正在做的事情,接下 ...

  7. 利用LruCache为GridView异步加载大量网络图片完整示例

    MainActivity如下: package cc.testlrucache; import android.os.Bundle; import android.widget.GridView; i ...

  8. 2016 - 1- 19 利用多线程优化从网上加载图片的Demo

    // // ZZTableViewController.m // 多图片下载 // // Created by Mac on 16/1/19. // Copyright © 2016年 Mac. Al ...

  9. 利用iscroll实现上拉加载下拉刷新

    1.首先引用isScroll插件 说明:页面加载时初始化isScroll,然后调用pullDownAction()和pullUpAction(),每次切换tab时,只需要对pullDownAction ...

随机推荐

  1. vue install 组件

    import share from './index.vue' export default { install: (Vue) => { Vue.prototype.$share = (opti ...

  2. ["Visual Studio快捷键" ,"Vs","IDEA快捷键"]

    描述说明 描述 说明 ↑ 方向键.上 ↓ 方向键.下 ← 方向键.左 → 方向键.右 快捷键大比拼 描述 Visual Studio 快捷键 IDEA快捷键 VisualStudio学名 IDEA学名 ...

  3. Java8 特性

    1.jdk8的特性stream().map() 2.Java8中用Lambda表达式的groupBy合并多个相同属性的对象集合 3.Java8 Stream 语法详解 & 用法实例

  4. c++动态链接问题

    https://blog.csdn.net/liu0808/article/details/81169173 https://blog.csdn.net/f110300641/article/deta ...

  5. my97整合fineui例子,开始和结束时间

      <f: Toolbar runat ="server">                             <Items>             ...

  6. 玩转 SpringBoot 2 快速整合 Filter

    概述 SpringBoot 中没有 web.xml, 我们无法按照原来的方式在 web.xml 中配置 Filter .但是我们可以通过 JavaConfig(@Configuration +@Bea ...

  7. MATLAB 安装 cvx 工具箱

    步骤: matlab本身是没有cvx的工具箱,需要到[cvx主页],「http://cvxr.com/cvx/」上下载,菜单上有个「download」,进入后选择适合你的版本下载: 将cvx压缩包解压 ...

  8. (转)为什么ssh一关闭,程序就不再运行了?

    ref :https://www.cnblogs.com/lomper/p/7053694.html 问题描述 当SSH远程连接到服务器上,然后运行一个程序,eg: ./test.sh, 然后把终端开 ...

  9. [Atcoder AGC037E]Reversing and Concatenating

    题目大意:有一个长度为$n$的字符串$S$,有$k$次操作,每次操作为把$S$变为$SS^R$(即翻转后再接在一起),然后从中选取一段长度为$n$的字串.问$k$次操作后,字典序最小的一种是什么.$n ...

  10. SqlServer调用OPENQUERY函数远程执行增删改查

    /* OPENQUERY函数,远程执行数据库增删改查 关于OPENQUERY函数第二个参数不支持拼接变量的方案 方案1:将OPENQUERY语句整个拼接为字符串,再用EXEC执行该字符串语句 方案2: ...