美文网首页
ViewStub基本用法

ViewStub基本用法

作者: 因为我的心 | 来源:发表于2020-11-08 11:28 被阅读0次

一、前言:

当我们需要根据某个条件控制某个View的显示或者隐藏的时候,通常是把可能用到的View都写在布局上,然后设置可见性为View.GONE或View.InVisible ,之后在代码中根据条件动态控制可见性。虽然操作简单,但是耗费资源,因为即便该view不可见,仍会被父窗体绘制,仍会创建对象,仍会被实例化,仍会被设置属性。

而android.view.ViewStub,是一个大小为0 ,默认不可见的控件,只有给他设置成了View.Visible或调用了它的inflate()之后才会填充布局资源,也就是说占用资源少。所以,推荐使用viewStub。

image.png

二、viewStub的特点:

    1. ViewStub只能Inflate一次,多次 inflate 会报错,之后我们可以控制ViewStub的显示和隐藏。
    1. ViewStub只能用来Inflate一个布局文件,而不是某个具体的View,当然也可以把View写在某个布局文件中。

三、代码使用:

下面来看一个实例

在这个例子中,要显示二种不同的布局,一个是用TextView显示一段文字,另一个则是用ImageView显示一个图片。这二个是在onCreate()时决定是显示哪一个,这里就是应用ViewStub的最佳地点。

先来看看布局,一个是主布局,里面只定义二个ViewStub,一个用来控制TextView一个用来控制ImageView,另外就是一个是为显示文字的做的TextView布局,一个是为ImageView而做的布局:

1、主布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center_horizontal">
    <ViewStub
        android:id="@+id/viewstub_demo_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dip"
        android:layout_marginRight="5dip"
        android:layout_marginTop="10dip"
        android:layout="@layout/viewstub_demo_text_layout"/>
    <ViewStub
        android:id="@+id/viewstub_demo_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dip"
        android:layout_marginRight="5dip"
        android:layout="@layout/viewstub_demo_image_layout"/>
</LinearLayout>

2、TextView的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/viewstub_demo_textview"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="#aa664411"
        android:textSize="16sp"/>
</LinearLayout>

3、为ImageView的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <ImageView
        android:id="@+id/viewstub_demo_imageview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

下面来看代码,决定来显示哪一个,只需要找到相应的ViewStub然后调用其infalte()就可以获得相应想要的布局:

4、MainActivity

public class MainActivity extends AppCompatActivity {
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if ((((int) (Math.random() * 100)) & 0x01) == 0) {
            // to show text
            // all you have to do is inflate the ViewStub for textview
            ViewStub stub = (ViewStub) findViewById(R.id.viewstub_demo_text);
            stub.inflate();
            TextView text = (TextView) findViewById(R.id.viewstub_demo_textview);
            text.setText("The tree of liberty must be refreshed from time to time" +
                    " with the blood of patroits and tyrants! Freedom is nothing but " +
                    "a chance to be better!");
        } else {
            // to show image
            // all you have to do is inflate the ViewStub for imageview
            ViewStub stub = (ViewStub) findViewById(R.id.viewstub_demo_image);
            stub.inflate();
            ImageView image = (ImageView) findViewById(R.id.viewstub_demo_imageview);
            image.setImageResource(R.mipmap.ic_launcher);
        }
    }
}

四、使用总结:

使用的时候的注意事项:

  1. 某些布局属性要加在ViewStub而不是实际的布局上面,才会起作用,比如上面用的android:layout_margin*系列属性,如果加在 TextView上面,则不会起作用,需要放在它的ViewStub上面才会起作用。而ViewStub的属性在inflate()后会都传给相应的布 局。
  1. ViewStub之所以常称之为“延迟化加载”,是因为在教多数情况下,程序 无需显示ViewStub所指向的布局文件,只有在特定的某些较少条件下,此时ViewStub所指向的布局文件才需要被inflate,且此布局文件直 接将当前ViewStub替换掉,具体是通过viewStub.infalte()或 viewStub.setVisibility(View.VISIBLE)来完成;
  1. viewStub.infalte()调用会首次展示页面;
  1. 对ViewStub的inflate操作只能进行一次,因为inflate的 时候是将其指向的布局文件解析inflate并替换掉当前ViewStub本身(由此体现出了ViewStub“占位符”性质),一旦替换后,此时原来的 布局文件中就没有ViewStub控件了,因此,如果多次对ViewStub进行infalte,会出现错误信息:ViewStub must have a non-null ViewGroup viewParent。

5.ViewStub本身是不可见的,对 ViewStub setVisibility(..)与其他控件不一样,ViewStub的setVisibility 成View.VISIBLE或INVISIBLE如果是首次使用,都会自动inflate其指向的布局文件,并替换ViewStub本身,再次使用则是相 当于对其指向的布局文件设置可见性。

注意:我们可以对ViewStub setVisibility(..)进行控制,完成布局的显示和隐藏。

五、问题

1、 viewStub的setVisibility()中也调用了inflate(),但是为什么多次调用setVisibility()不会导致崩溃呢?

ViewStub 的setVisibility() 方法中,会先判断 WeakReference 类型的成员变量 mInflatedViewRef 是否为空。第一次调用setVisibility()的时候,mInflatedViewRef并没有初始化,也就是说是null,那么这时候就会走inflate(),在inflate() 方法中给被填充起来的布局/view创建一个WeakReference弱引用,并赋值给mInflatedViewRef,从而完成mInflatedViewRef的初始化。当第二次走setVisibility() 的时候,mInflatedViewRef已经不再是null,就会调用 WeakReference 的父类Reference 中的get() 方法获取该引用指向的实体对象,也就是说通过get() 拿到 被填充的view对象,然后再走View类的setVisibility()。

**ViewStub类中的setVisibility()具体实现如下:**
@Override
@android.view.RemotableViewMethod
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();
        }
    }
}

参考:https://blog.csdn.net/qq_36282231/article/details/82721018

相关文章

网友评论

      本文标题:ViewStub基本用法

      本文链接:https://www.haomeiwen.com/subject/acthbktx.html