ViewStub 懒加载
最开始接触ViewStub
是从DecorView
中认识的,都知道DecorView是顶级的View,它是一个线性布局,它有两个子View:ViewStub
和FrameLayout
,帧布局中包括一个FitWindowsLinearLayout
布局,FitWindowsLinearLayout
包含ViewStubCompat
和ContentFrameLayout
,而我们使用setContentView放进去的布局文件就是放在ContentFrameLayout这个布局之中。而我们最开始就接受到的知识是DecorView是两个部分组成的,一个是titleView,另一个就是ContentView
(可以用findViewById(android.R.id.content
)获取)。
ViewStub也是继承自View,它使用的是惰性加载的方式,也就是即使它已经包含于布局文件中,但是不主动加载,它就是为空的。ViewStub一般用于需要展示另外的一个效果,如网络加载失败的页面显示。
ViewStub的加载原理
ViewStub
只能加载一次,重复加载会导致异常,这是因为ViewStub只要加载一次自身就会被移除,并把自身所包含的内容传递给父布局。1
2
3
4
5<ViewStub
android:id="@+id/vs"
android:layout="@layout/activity_detail"
android:layout_width="match_parent"
android:layout_height="match_parent" />
调用的ViewStub的inflate()
方法就会加载布局,还有一种加载它的方法,在它的setVisibility
方法的源码中可以看到。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public 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();
}
}
}
当你设置visibility为VISIBLE
或INVISIBLE
就会调用inflate()去加载
include
include的用法也很简单,它的出现提高了布局的可重用性,使得更方便控制管理。1
2
3
4<include
layout="@layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
使用include可能遇到的坑:
1、include标签当中可以重写layout的所有属性,在里面覆写的layout属性会覆盖掉实际layout(如上就是app_bar_main)布局的layout属性),另外需要注意的是如果要覆写layout属性的话,必须要覆写layout_width
和layout_height
,Android Studio一般也会提示。
2、一个布局文件中如果有多个include标签需要设置id才能找到对应的子控件,否则只能找到第一个include的布局文件及控件
3、如果你在include标签的地方和加载的根布局文件都设置了id,需要保持一致,否则可能获取不到根布局对象。
merge
merge标签是配和include标签使用的,作为include的根布局标签,意义是帮助include标签去除一层多余的ViewGroup,具体很简单就不用多说,想想就明白了。
使用merge的注意事项:
1、根布局是FrameLayout
且不需要设置background
或padding
等属性,可以用merge标签代替,因为setContentView
就是加载到帧布局之中的。
2、因为merge不是View,所以在通过LayoutInflate.inflate()
方法渲染的时候,第二个参数必须指定一个父容器
,第三个参数必须为true
,由于merge不是View,所以对merge标签的所有设置都是无效的。
附录:一个求Activity最大层级的算法
在网上看到过一个递归版本的这种算法,那既然递归版本的有了,就来一个非递归版本的。其实这个算法可以看做是一颗树(因为View的层级就是一棵View树),那其实就是利用树的层次遍历,算出最深的节点,树的层次遍历怎么实现也就很简单拉,就是把每一层从左到右依次放入一个队列中,从队列头部取出,然后将它的子节点放到队列末尾,一直循环执行下去知道队列为空。那我们并不要这个层次遍历输出的结果,我们只需要最大深度该怎么实现,难搞啊。
不慌,问题不大,我们可以在一层子节点的队列尾部加上一个标志,等到队列的同级的节点都出了队列的时候,就到了这个标志,就意味着一层已经遍历完了,然后再将这个标志移到队列的尾部,继续执行,那不就行了,但是要注意,如果没有下一层,那么一直移动就会造成死循环,所以排除掉这种情况。好,下面上代码:
1 | private void findDeep() { |