ViewStub

参考

概述

        ViewStub是一种不可视,并且大小为0的视图,可以延迟到运行时填充(inflate)布局资源。当ViewStub被设置为可视或者inflate()被调用后,就会填充布局资源,然后ViewStub便会被填充的视图替代。
        ViewStub是实现视图延迟加载的优秀类。无论在什么情况下,只要开发者需要根据上下文选择隐藏或者显示一个视图,都可以用ViewStub实现 或许并不会因为一个视图的延迟加载而感觉到性能 明显提升,但是如果视图树的层次很深,便会感觉到性能上的差距了。
        以上两段摘自《Android开发必知的50个诀窍》

注意

        一个ViewStub只能inflate()一次
        在布局文件中使用ViewStub时,必须指定layout属性的值。
        ViewStub被inflate()后,它所指向的布局会替换掉它在父布局中的位置。
        它所指向的布局的根元素的一些布局参数,必须在<ViewStub>中进行设置,如:layout_width,layout_height等。
        为<ViewStub>设置的id会被设置为布局文件生成的布局的根元素的id

使用

        在布局中使用ViewStub非常简单,跟使用TextView一样。但 必须指定layout属性,且该属性必须指向一个布局文件。
        可以指定inflateId属性,该属性是用来更新布局文件的根元素的id。(Overrides the id of the inflated View with this value)。
        在ViewStub中构造方法中可以发现:指定的layout属性会赋值给ViewStub类中的 mLayoutResource变量,inflateId会被赋值给 mInflatedId变量。代码如下:
        mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
        mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
        在代码中调用ViewStub.inflate()就可以显示ViewStub引用的布局。

分析

ViewStub.inflate()

    public View inflate() {
        final ViewParent viewParent = getParent();//首先得到ViewStub的父布局
        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);//将inflateId属性的值设置成布局文件中的根元素的id。代码五
                }

                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>(view);

                if (mInflateListener != null) {
                    mInflateListener.onInflate(this, view);
                }

                return view;
            } else {//如果没有指定layout属性,这里并报错。因此,必须指定layout属性,无论是通过xml还是代码
                throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
            }
        } else {
            throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
        }
    }
        在代码一处:通过指定的布局资源生成新的View。因此,ViewStub的layout属性必须存在,且必须指向一个布局文件。而且在inflate()的最后会把通过布局文件生成的视图返回。
        在代码三、四处:首先获得ViewStub在父布局中的下标,其次将ViewStub从父布局中移除,然后将布局文件生成的视图添加到父布局中。也就是:用布局文件生成的布局会替换掉ViewStub在父布局中的位置
        通过代码三、四还可以看出:ViewStub一旦被inflate后,就会从父布局中移除掉,因而一个ViewStub只能inflate一次。因为再次inflate时会直接走到最下面的else中。
        在代码四处,将生成的布局添加到ViewStub的父布局时传递了ViewStub自身的LayoutParams。因此布局文件中的根结点的有些布局参数必须在<ViewStub>中设置(如layout_width,layout_height等),但并不是所有的以layout_开头的属性都需要在<ViewStub>中设置。
        在代码五处,将ViewStub的id设置为生成的布局的id

setVisibility()

        调用ViewStub.setVisibility(View.VISIBLE)时,它所执行的效果和ViewStub.inflate()一样。示例如下:
		View view = findViewById(R.id.myid);//代码一
		// view stub replaced with inflated layout (if stub is used in layout)
		view.setVisibility(View.VISIBLE);//代码二
		if (view.getParent() == null) {//代码三
			// a stub was used so we need to find the newly inflated view that
			// replaced it
			view = findViewById(R.id.myid);//代码四
		} else {
			// nothing to do, the view we found the first time is what we want
		}
        代码一:R.id.myid是在布局中为ViewStub设置的id。但是这里并没有强转成ViewStub,这是因为代码二处的是任何View都有的方法。
        代码二:无论代码一处得到的是ViewStub还是别的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();
            }
        }
    }
        从源代码中可以看出:此时实际上也是调用了inflate()。而inflate()中会将ViewStub的id设置为生成的布局的id。这也保证了在代码四处得到的是最新的生成的布局,而不是ViewStub。
        之所以加上代码三是为了优化代码。如果代码二得到的不是ViewStub,那么代码四处就相当于重复调用,从而影响了性能。在ViewStub.inflate()时,会将ViewStub从它的父布局中移除,因此如果代码一得到的是ViewStub那么代码三才成立,从而执行代码四,保证了得到的View是通过布局文件生成的新布局。

缺点

        由于ViewStub只能inflate一次,因此,对于那些需要多次显示隐藏的控件,只能交替使用GONE与VISIBLE。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
viewStub是一个轻量级的View,它可以延迟加载布局资源,而不必在Activity或Fragment的onCreate方法中立即加载视图树,这样可以提高应用程序的性能。而viewBinding是一种新的方式,它允许您直接从布局文件中获取对视图的引用,而不必使用findViewById()方法。当您使用viewStub时,您需要在布局文件中定义一个viewStub元素,然后在代码中使用它来加载布局资源。而当您使用viewBinding时,您需要在Activity或Fragment的onCreate方法中初始化绑定对象,然后使用它来获取对布局文件中的视图的引用。如果您要在使用viewBinding的情况下使用viewStub,您可以使用ViewBinding.inflate()方法来创建ViewBinding对象,然后使用ViewBinding.getRoot()方法来获取根视图,然后将其传递给viewStub的setVisibility()方法来显示或隐藏它。例如,您可以使用以下代码来显示viewStub: ``` private lateinit var binding: ActivityMainBinding private lateinit var stub: ViewStub override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) stub = binding.viewStub val inflatedView = binding.viewStub.inflate() // Do something with inflatedView } ``` 在这个例子中,我们首先使用ViewBinding.inflate()方法初始化了绑定对象,然后使用ViewBinding.getRoot()方法获取根视图,并将其传递给viewStub的setVisibility()方法来显示它。然后,我们使用viewStubinflate()方法来加载布局资源,并将返回的视图对象保存在一个变量中,以便我们可以在代码中使用它。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值