Android ViewStub
01. 简介
A ViewStub is an invisible, zero-sized View that can be used to lazily inflate layout resources at runtime.
控件 ViewStub 是一个不可见的,零尺寸的 View 它可以在运行时进行延迟加载。
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.
当 ViewStub 的 setVisibility(int) 方法或者 inflate() 方法被调用,它会加载被指定的布局并在父布局中将自己替换为加载的布局。
Therefore, the ViewStub exists in the view hierarchy until setVisibility(int) or inflate() is invoked.
替换后,控件 ViewStub 会从布局树中移除。
The inflated View is added to the ViewStub's parent with the ViewStub's layout parameters.
控件 ViewStub 的布局属性会传递给被加载的布局。
因此,不是必须显示的布局使用 ViewStub 代替后减少界面首次加载时资源消耗,提升最初加载速度。
02. 用法
布局
android:id="@+id/viewStub"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="10dp"
android:inflatedId="@+id/titleBar"
android:layout="@layout/just_title" />
android:layout 指定被加载替换的布局
android:inflatedId 指定被加载替换的布局的 id
加载
viewStub = (ViewStub) findViewById(R.id.viewStub);
View inflated = stub.inflate();
官方推荐加载首选方法
调用 inflate() 方法后布局被加载替换同时返回布局对象。避免了使用 findViewById() 方法。
该 inflate() 方法只能调用一次,调用被移除而没有了父布局。第二次调用会抛出异常 ViewStub must have a non-null ViewGroup viewParent 。
03. 示例
主界面布局
android:layout_width="match_parent"
android:layout_height="match_parent">
android:id="@+id/viewStub"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="10dp"
android:inflatedId="@+id/titleBar"
android:layout="@layout/just_title" />
android:id="@+id/bt_show"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="80dp"
android:text="Show"
android:textAllCaps="false" />
android:id="@+id/bt_hide"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/bt_show"
android:layout_marginTop="10dp"
android:text="Hide"
android:textAllCaps="false" />
被替换布局 just_title.xml
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/colorPrimary">
android:id="@+id/tv_show_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:ellipsize="end"
android:singleLine="true"
android:textColor="@android:color/white"
android:textSize="20sp"
tools:text="Title" />
界面加载
public class ViewStubActivity extends AppCompatActivity implements View.OnClickListener {
private ViewStub viewStub;// 占位控件
private Button bt_show;// 显示按钮
private Button bt_hide;// 隐藏按钮
private TextView tv_show_title;// 标题
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_stub);
viewStub = (ViewStub) findViewById(R.id.viewStub);// 寻找控件
bt_show = (Button) findViewById(R.id.bt_show);
bt_hide = (Button) findViewById(R.id.bt_hide);
bt_show.setOnClickListener(this);
bt_hide.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_show:// 显示
try {
View titleBar = viewStub.inflate();// 第二次加载会抛出异常
tv_show_title = (TextView) titleBar.findViewById(R.id.tv_show_title);
tv_show_title.setText("Title");
} catch (Exception e) {
viewStub.setVisibility(View.VISIBLE);
}
break;
case R.id.bt_hide:// 隐藏
viewStub.setVisibility(View.GONE);
break;
}
}
}
隐藏
ViewStubHide
显示
ViewStubShow
更换加载方式
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_show:// 显示
viewStub.setVisibility(View.VISIBLE);// 方式二
if (tv_show_title == null) {
tv_show_title = (TextView) findViewById(R.id.tv_show_title);
tv_show_title.setText("Title");
}
break;
}
}
04. 监听
加载监听回调
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_stub);
viewStub = (ViewStub) findViewById(R.id.viewStub);// 寻找控件
// 设置加载监听回调,成功加载后回调[只会回调一次]
viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
/**
* Listener used to receive a notification after a ViewStub has successfully inflated its layout resource.
*
* @param stub ViewStub 对象
* @param inflated 被加载填充的布局
*/
@Override
public void onInflate(ViewStub stub, View inflated) {
tv_show_title = (TextView) inflated.findViewById(R.id.tv_show_title);
tv_show_title.setText("ShowTitle");
}
});
}
按需使用
05. 源码
setVisibility() 方法
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() 方法
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) {// 被指定的布局的 ID
view.setId(mInflatedId);// 即 android:inflatedId 指定的新 ID
}
final int index = parent.indexOfChild(this);// 获取位置
parent.removeViewInLayout(this);// 移除 ViewStub 自己
final ViewGroup.LayoutParams layoutParams = getLayoutParams();// ViewStub 指定的布局参数
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");
}
}
06. 注意
ViewStub 支持使用 标签的布局。
ViewStub 不支持使用 标签的布局。
07. 参考