废话不多说,先来看看官方文档说明:
A ViewStub is an invisible, zero-sized View that can be used to lazily inflate layout resources at runtime. When a ViewStub is made visible, or when
inflate()
is invoked, the layout resource is inflated. The ViewStub then replaces itself in its parent with the inflated View or Views. Therefore, the ViewStub exists in the view hierarchy until
setVisibility(int)
or inflate()
is invoked. The inflated View is added to the ViewStub's parent with the ViewStub's layout parameters. Similarly, you can define/override the inflate View's id by using the ViewStub's inflatedId
property.
首先它是一个继承view的子类,ViewStub是一个不可见的零大小的视图,可用于在运行时延迟布局资源。 当ViewStub变为可见时,或者当调用inflate()时,布局资源会被填充。 ViewStub然后在其父级中用填充的视图替换它自己。 因此,ViewStub存在于视图层次结构中,直到调用setVisibility(int)或inflate()。 填充的视图被添加到ViewStub的父视图和ViewStub的布局参数。 类似地,您可以使用ViewStub的inflatedId属性定义/覆盖inflate View的id。
总结起来比较简单,StubView就是一个延时加载的默认不显示的控件。
使用的一些注意事项:
1,判断是否已经加载过, 如果通过setVisibility来加载,那么通过判断可见性即可;如果通过inflate()来加载是不可以通过判断可见性来处理的,而需要使用方式2来进行判断。
2,findViewById的问题,注意ViewStub中是否设置了inflatedId,如果设置了则需要通过inflatedId来查找目标布局的根元素。
应用:例如我们通过一个ViewStub来惰性加载一个消息流的评论列表,因为一个帖子可能并没有评论,此时我可以不加载这个评论的ListView,只有当有评论时我才把它加载出来,这样就去除了加载ListView带来的资源消耗以及延时,示例如下 :
<span style="font-size:14px;"><ViewStub android:id="@+id/stub_import" android:inflatedId="@+id/stub_comm_lv" android:layout="@layout/my_comment_layout" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" / my_comment_layout.xml如下: </span><pre class="prettyprint" name="code"><span style="font-size:14px;"><code class="language-xml hljs has-numbering"><span class="hljs-pi"><?xml version="1.0" encoding="utf-8"?></span> <span class="hljs-tag"><<span class="hljs-title">ListView</span> <span class="hljs-attribute">xmlns:android</span>=<span class="hljs-value">"http://schemas.android.com/apk/res/android"</span> <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span> <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/my_comm_lv"</span> <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"match_parent"</span> ></span> <span class="hljs-tag"></<span class="hljs-title">ListView</span>></span> </code></span>
在运行时,我们只需要控制id为stub_import的ViewStub的可见性或者调用inflate()函数来控制是否加载这个评论列表即可。示例如下 :
public class MainActivity extends Activity { public void onCreate(Bundle b){ // main.xml中包含上面的ViewStub setContentView(R.layout.main); // 方式1,获取ViewStub, ViewStub listStub = (ViewStub) findViewById(R.id.stub_import); // 加载评论列表布局 listStub.setVisibility(View.VISIBLE); // 获取到评论ListView,注意这里是通过ViewStub的inflatedId来获取 ListView commLv = findViewById(R.id.stub_comm_lv); if ( listStub.getVisibility() == View.VISIBLE ) { // 已经加载, 否则还没有加载 } } }
通过setVisibility(View.VISIBILITY)来加载评论列表,此时你要获取到评论ListView对象的话,则需要通过findViewById来查找,而这个id并不是就是ViewStub的id。
这是为什么呢 ?
我们先看ViewStub的部分代码吧:
<pre class="prettyprint" name="code"><span style="font-size:14px;"><code class="language-java hljs has-numbering"> <span class="hljs-annotation">@SuppressWarnings</span>({<span class="hljs-string">"UnusedDeclaration"</span>}) <span class="hljs-keyword">public</span> <span class="hljs-title">ViewStub</span>(Context context, AttributeSet attrs, <span class="hljs-keyword">int</span> defStyle) { TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ViewStub, defStyle, <span class="hljs-number">0</span>); <span class="hljs-comment">// 获取inflatedId属性 </span> mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID); mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, <span class="hljs-number">0</span>); a.recycle(); a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View, defStyle, <span class="hljs-number">0</span>); mID = a.getResourceId(R.styleable.View_id, NO_ID); a.recycle(); initialize(context); } <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">initialize</span>(Context context) { mContext = context; setVisibility(GONE);<span class="hljs-comment">// 设置不可教案 </span> setWillNotDraw(<span class="hljs-keyword">true</span>);<span class="hljs-comment">// 设置不绘制 </span> } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onMeasure</span>(<span class="hljs-keyword">int</span> widthMeasureSpec, <span class="hljs-keyword">int</span> heightMeasureSpec) { setMeasuredDimension(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>);<span class="hljs-comment">// 宽高都为0 </span> } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setVisibility</span>(<span class="hljs-keyword">int</span> visibility) { <span class="hljs-keyword">if</span> (mInflatedViewRef != <span class="hljs-keyword">null</span>) {<span class="hljs-comment">// 如果已经加载过则只设置Visibility属性 </span> View view = mInflatedViewRef.get(); <span class="hljs-keyword">if</span> (view != <span class="hljs-keyword">null</span>) { view.setVisibility(visibility); } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalStateException(<span class="hljs-string">"setVisibility called on un-referenced view"</span>); } } <span class="hljs-keyword">else</span> {<span class="hljs-comment">// 如果未加载,这加载目标布局 </span> <span class="hljs-keyword">super</span>.setVisibility(visibility); <span class="hljs-keyword">if</span> (visibility == VISIBLE || visibility == INVISIBLE) { inflate();<span class="hljs-comment">// 调用inflate来加载目标布局 </span> } } } <span class="hljs-javadoc">/** * Inflates the layout resource identified by {@link #getLayoutResource()} * and replaces this StubbedView in its parent by the inflated layout resource. * *<span class="hljs-javadoctag"> @return</span> The inflated layout resource. * */</span> <span class="hljs-keyword">public</span> View <span class="hljs-title">inflate</span>() { <span class="hljs-keyword">final</span> ViewParent viewParent = getParent(); <span class="hljs-keyword">if</span> (viewParent != <span class="hljs-keyword">null</span> && viewParent <span class="hljs-keyword">instanceof</span> ViewGroup) { <span class="hljs-keyword">if</span> (mLayoutResource != <span class="hljs-number">0</span>) { <span class="hljs-keyword">final</span> ViewGroup parent = (ViewGroup) viewParent;<span class="hljs-comment">// 获取ViewStub的parent view,也是目标布局根元素的parent view </span> <span class="hljs-keyword">final</span> LayoutInflater factory = LayoutInflater.from(mContext); <span class="hljs-keyword">final</span> View view = factory.inflate(mLayoutResource, parent, <span class="hljs-keyword">false</span>);<span class="hljs-comment">// 1、加载目标布局 </span> <span class="hljs-comment">// 2、如果ViewStub的inflatedId不是NO_ID则把inflatedId设置为目标布局根元素的id,即评论ListView的id </span> <span class="hljs-keyword">if</span> (mInflatedId != NO_ID) { view.setId(mInflatedId); } <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> index = parent.indexOfChild(<span class="hljs-keyword">this</span>); parent.removeViewInLayout(<span class="hljs-keyword">this</span>);<span class="hljs-comment">// 3、将ViewStub自身从parent中移除 </span> <span class="hljs-keyword">final</span> ViewGroup.LayoutParams layoutParams = getLayoutParams(); <span class="hljs-keyword">if</span> (layoutParams != <span class="hljs-keyword">null</span>) { parent.addView(view, index, layoutParams);<span class="hljs-comment">// 4、将目标布局的根元素添加到parent中,有参数 </span> } <span class="hljs-keyword">else</span> { parent.addView(view, index);<span class="hljs-comment">// 4、将目标布局的根元素添加到parent中 </span> } mInflatedViewRef = <span class="hljs-keyword">new</span> WeakReference<View>(view); <span class="hljs-keyword">if</span> (mInflateListener != <span class="hljs-keyword">null</span>) { mInflateListener.onInflate(<span class="hljs-keyword">this</span>, view); } <span class="hljs-keyword">return</span> view; } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"ViewStub must have a valid layoutResource"</span>); } } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalStateException(<span class="hljs-string">"ViewStub must have a non-null ViewGroup viewParent"</span>); } }</code></span>置为根元素的id,这也是为什么我们在获取评论ListView时会使用findViewById(R.id.stub_comm_lv)来获取,其中的stub_comm_lv就是ViewStub的inflatedId。当然如果你没有设置inflatedId的话还是可以通过评论列表的id来获取的,例如findViewById(R.id.my_comm_lv)。然后就是ViewStub从parent中移除、把目标布局的根元素添加到parent中。最后会把目标布局的根元素返回,因此我们在调用inflate()函数时可以直接获得根元素,省掉了findViewById的过程。
参考:http://blog.csdn.net/bboyfeiyu/article/details/45869393