android中,很多地方在提到布局优化都会提到使用ViewStub.可能很多人都只是用她,而没有去看看ViewStub到底是个什么东西,器特殊的功能是如何实现的!这里,我来解析一下我们的ViewStub.
打开ViewStub.java,你会发现其实ViewStub就是一个view的子类,和其他framework的java文件一样,google都给了一个说明.其实通过这个说明,你基本就知道ViewStub的属性和使用方法来.这里我就通过她的说明开始入手.
下面我们来看第一段原文说明:
* A ViewStub is an invisible, zero-sized View that can be used to lazily inflate
* layout resources at runtime.
这一句英文说明指出来ViewStub的几乎所有特点:(1)不可见的(invisible);(2)没有大小的(zero-sized);(3)能够用于在运行时候延迟加载的.下面来看看,她是如何实现这三个特点的!
(1)不可见的(invisible): 在初始化ViewStub的时候,构造函数就会调用这个方法:initialize,来把这个view(ViewStub)设置为GONE,请看如下源码:
private void initialize(Context context) {
mContext = context;
setVisibility(GONE);
setWillNotDraw(true);
}
setVisibility(GONE);就时把这个view设置成GONE,也就是说我们这个ViewStub默认情况下就是会被初始化成一个GONE的view,这样以来在布局文件加载的时候,这个ViewStub被视为不可见的!
此外还调用了这个方法:setWillNotDraw(true);这样一来,该view的onDraw就不会执行!
其实,我个人认为:原文的说明:A ViewStub is an invisible,其实不仅仅是调用上面这两个方法来实现的, 通过查看ViewStub这个类,你会发现,ViewStub来从写来父类(view)的两个方法draw和dispatchDraw以及onMeasure, 以此来覆盖父类的这两个方法.下面是ViewStub对方法draw和dispatchDraw以及onMeasure从写的源码:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(0, 0);
}
@Override
public void draw(Canvas canvas) {
}
@Override
protected void dispatchDraw(Canvas canvas) {
}
看了上面代码,你肯定会吃惊,draw和dispatchDraw都是什么也不做! 并且onMeasure还什么也不做,直接setMeasuredDimension(0, 0);来把view区域设置位0. 看到这几行源码你就不得不明白,原来一个ViewStub虽然是一个view,却是一个没有任何显示内容,也不显示任何内容的特殊view,并且对layout在加载时候不可见的.
原文的说明还有最后一段:lazily inflate layout resources at runtime. 延迟inflate? 在运行的时候?
来看看接下来的一段说明:
* When a ViewStub is made visible, or when {@link #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 {@link #setVisibility(int)} or
* {@link #inflate()} is invoked.
上面的大意是: 当把这个ViewStub设置为visible,或者调用inflate()的时候,这个ViewStubde 的layout就会inflated,并且用inflated出的view替换原来ViewStub在整个布局的位置.
说起来有点复杂,我们来看看下面的源码:
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();
}
}
}
重写来父类的方法,通过上面方法可以知道,如果mInflatedViewRef为null,并且visibility == VISIBLE || visibility == INVISIBLE) 就会调用的inflate(); 实际上inflate()做的就是初始化这个ViewStub的内容,并且替换自己(ViewStub), 这里的mInflatedViewRef其实就是inflate()初始化的内容view,所以在上面的setVisibility首先要看看这个ViewStub是否已经inflate()了.
下面来看看inflate()这个方法的源码:
public View inflate() {
final ViewParent viewParent = getParent();
if (viewParent != null && viewParent instanceof ViewGroup) {
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);
if (mInflatedId != NO_ID) {
view.setId(mInflatedId);
}
final int index = parent.indexOfChild(this);
parent.removeViewInLayout(this);
final ViewGroup.LayoutParams layoutParams = getLayoutParams();
if (layoutParams != null) {
parent.addView(view, index, layoutParams);
} else {
parent.addView(view, index);
}
mInflatedViewRef = new WeakReference(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");
}
}
通过上面的代码,你应该很清楚了,这里的
mLayoutResource其实在ViewStub初始化的时候就会被赋值的.看看源码:
public ViewStub(Context context, AttributeSet attrs, int defStyle) {
TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ViewStub,
defStyle, 0);
mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
a.recycle();
a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View, defStyle, 0);
mID = a.getResourceId(R.styleable.View_id, NO_ID);
a.recycle();
initialize(context);
}
google其实还给出了使用方法的说明:
* ViewStub stub = (ViewStub) findViewById(R.id.stub);
* View inflated = stub.inflate();