利用ViewStub实现布局懒惰加载
这个问题也是头条面试官问的,本身没什么难度,但以前确实没仔细研究过。
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实现布局懒惰加载的更多相关文章
- 利用青瓷布局自定义加载的场景,而不是自己改写qici-loading
加载界面如果全部通过自己手动布局不仅不美观,还很难控制.借用原生的场景切换加载效果,来实现我们游戏的加载效果. 没有做加载修改的原来的加载顺序: 黑乎乎界面->(游戏定制的加载)你的第一个场 ...
- 利用简洁的图片预加载组件提升h5移动页面的用户体验
在做h5移动页面,相信大家一定碰到过页面已经打开,但是里面的图片还未加载出来的情况,这种问题虽然不影响页面的功能,但是不利于用户体验.抛开网速的原因,解决这个问题有多方面的思路:最基本的,要从http ...
- Android 实现布局动态加载
Android 动态加载布局 通过使用LayoutInflater 每次点击按钮时候去读取布局文件,然后找到布局文件里面的各个VIEW 操作完VIEW 后加载进我们setContentView 方面里 ...
- MVC学习七:Razor布局之加载分部视图【PartialView】
Partial View 顾名思义就是Html代码片段,应用于此HTML代码多次被页面加载时使用.(类似于WebForm程序中的用户控件) 注:PartialView和正常的View页面在访问时没有任 ...
- C# winform利用反射和自定义特性加载功能模块(插件式开发)
由于在实际的工作中, 碰见这样的一个问题: 一个软件, 销售给A客户 他需要所有功能, 但是销售给B客户, 他只需要其中的一部分, 1.如果我们在实际的开发过程中, 没有把一些功能模块区分开来的话, ...
- 利用Navigation Timing测量页面加载时间
最近在看一本名为<web性能实践日志>的书籍,其中第十三章"网络计时"中介绍了一种比较新的计算页面各部分加载时间方法,这也是W3C Web性能工作小组正在做的事情,接下 ...
- 利用LruCache为GridView异步加载大量网络图片完整示例
MainActivity如下: package cc.testlrucache; import android.os.Bundle; import android.widget.GridView; i ...
- 2016 - 1- 19 利用多线程优化从网上加载图片的Demo
// // ZZTableViewController.m // 多图片下载 // // Created by Mac on 16/1/19. // Copyright © 2016年 Mac. Al ...
- 利用iscroll实现上拉加载下拉刷新
1.首先引用isScroll插件 说明:页面加载时初始化isScroll,然后调用pullDownAction()和pullUpAction(),每次切换tab时,只需要对pullDownAction ...
随机推荐
- CentOS7使用yum安装redis
#下载fedora的epel仓库yum install epel-release #安装redis数据库yum install redis # 启动Redisservice redis start # ...
- 【漫谈数据仓库】 如何优雅地设计数据分层 ODS DW DM层级
转载http://bigdata.51cto.com/art/201710/554810.htm 一.文章主题 本文主要讲解数据仓库的一个重要环节:如何设计数据分层!其它关于数据仓库的内容可参考之前的 ...
- C++排序算法比较
排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. ...
- electron实现MessageBox
1.在渲染进程引用主进程模块 var remote = require('electron').remote; var dialog = remote.dialog; 2.实现一点简单的确定取消操作 ...
- PHP防止sql语句注入终极解决方案(包含pdo各种操作使用实例)
PHP防止sql语句注入终极解决方案完美解决方案就是使用拥有Prepared Statement机制(预处理sql)的PDO //先做个实验 先不用预处理sql写法<pre><?ph ...
- Beautiful Soup库入门
1.安装:pip install beautifulsoup4 Beautiful Soup库是解析.遍历.维护“标签树”的功能库 2.引用:(1)from bs4 import BeautifulS ...
- [转帖]Linux超级用户root口令忘记怎么办?
Linux超级用户root口令忘记怎么办? 2010-05-10 12:15:00 monkey_d_meng 阅读数 5535 收藏 更多 分类专栏: Linux 版权声明:本文为博主原创文章 ...
- windows系统转linux系统后磁盘的处理
背景: 原服务器是windows操作系统的.在没有进行格式化的情况下,重新安装了linux系统.比如centos7后,磁盘该怎么格式化?以下是演示过程. 1. 查看磁盘情况: # fdisk -l 2 ...
- vue 项目不显示样式 排版错乱
vue中的css 样式都在index.html中 看这里是否有导入css
- Sqlite清空表数据以及重新设置主键操作
Sqlite清空表数据以及重新设置主键操作 delete from 表名; //清空数据 update sqlite_sequence SET seq = 0 where name ='表名';//自 ...