ViewStub的那些事儿

     ViewStub是一个很好用的优化控件,在一些业务复杂,layout里面的各种View很多,而且有很多View都是根据不同情况显示或者隐藏甚至用不到时,如果在Activity启动开始就加载进来时,通过打日志可以看出很慢,经常超过100ms,这样用户在使用软件时会明显感觉到卡,所以今天要介绍一个优化神器—ViewStub

     ViewStub给我第一反应就是它与merge 以及include的相同与区别是什么?相同点就是他们都是辅助性的工具,用来过渡的,都可以写在xml里面,区别是merge和include只能写在xml里面,写完运行后android自动解析,而ViewStub还可以代码里调用,因为它继承自View,做一些事情,再者他们的作用和应用场景不同,include的作用是为了xml能够复用,我这个layout调用了公共的xml,那个也可以调用前面同样的xml,只需要在自己的代码include就行,而在实践中发现有时layout的层数太多,缺点是include的顶层与加载它的父layout中包含include的那个类型相同时,没办法自动去掉重复,怎么办?merge解决了这个问题,merge有include的功能,就是把自己模块化,并且自己完成任务以后不再是一个层,这样就解决了重复层这个问题,在实践中发现,有的View有时需要显示,有时隐藏,甚至有时用不到,那么基本性能考虑,如果隐藏或者用不到,我能不能不加载呢?这样的想法确实很好,而merge和include解决不了,怎么办?那就是我们的ViewStub要出场了,ViewStub刚好可以解决这个问题,它先在某个你指定的地方占个位,当你需要时,你可把自己想要的View加载上来,如果不需要,只让它在那里占个位,内存小,这就是按需加载(这是我对三者的相同与区别,有不同意见的,欢迎在下面评论,让我也补补,哈哈),为了兄弟们看了更清楚,我弄了张图:


从上图可以看出,ViewStub可写在xml里面,那要怎么写呢?接下来我们就要研究一下它,ViewStub在xml里面最显著的两个属性:android:inflatedId和android:layout,前者是将来要填充View的id,我一般都是ViewStub的id相同,后者是将来要填充View的xml文件

android:inflatedId对应ViewStub代码里的setInflatedId()函数,而android:layout对应该ViewStub代码里的setLayoutResource()函数,意思就是实现了xml的相应属性,在后面代码里就可以不调用相应的代码,因为重复了。而xml的ViewStub里设置的LayoutParams属性值都会被后面填充进来的View给沿用,当时我做项目时就以为ViewStub都被删除了,应该它的LayoutParams属性也应该没有了,后来看源码,发现沿用了,如下代码:

finalViewGroup.LayoutParams layoutParams= getLayoutParams();
if
(layoutParams != null) {
    parent.addView(view
, index,layoutParams);
} else {
    parent.addView(view
, index);
}

上面代码可以见于ViewStub. inflate()函数内

所以要把layout_width,layout_height,Margin, layout_below,layout_centerInParent之类的都设置到ViewStub属性里,这样会被将来被填充的View给沿用。

   ViewStub因为继承自View,所以View能有的属性和函数它都有,这就不介绍了,现在我们来看看,它的特别之处: setOnInflateListener()和inflate()两函数

setOnInflateListener()函数,咱们看看源码:

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

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

看到了倒数2-4行了么,这个就是,也就是它干完所以的事以后,当ViewStub.inflate()函数返回view之前做一个回调,我个人暂时没有发现它的妙处在哪,因为view返回以后,也可以自己知道了。

   接下来,我要重点讲下ViewStub. inflate()函数,它就是关键的那么一下,代码如下:

public View inflate() {
    final ViewParent viewParent = getParent();//拿到ViewStub的父ViewGroup

    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);
            }
              //下面就是把之前android:layout设置的撑开得到view
            final View view = factory.inflate(mLayoutResource, parent,
                    false);

            if (mInflatedId != NO_ID) {
                view.setId(mInflatedId);
            }
                      //上面就是把android:inflatedId设置的给设置上
            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);
            }
                    //上面就是沿用xml里面ViewStub设置的ViewGroup.LayoutParams属性
            mInflatedViewRef = new WeakReference<View>(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");
    }
}

最后说一下ViewStub. inflate()与ViewStub.setVisibility()都可以实现在view没有撑开之前,调用后撑开,但还是有区别,咱们来看看ViewStub. 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();
        }
    }
}

if(visibility == VISIBLE || visibility == INVISIBLE) {

            inflate();

        }

看到这段后,我就觉得使用ViewStub. setVisibility()好一些,为什么?因为当ViewStub不需要时,而上层又setVisibility()并传View.GONE时,这时不会创建撑开View,省了!第二个原因是它如果已经撑开过,如果这时再调用inflate()会得到这个异常:thrownew IllegalStateException("ViewStub must have a non-null ViewGroupviewParent");而ViewStub.setVisibility()这个不会。

   下面把知道分享完了,把例子代码也贴贴:

Xml:

<ViewStub
    android:id="@+id/myViewStub"
    android:layout_width="80dp"
    android:layout_height="80dp"
    android:layout_marginTop="20dp"
    android:layout_centerInParent="true"
    android:layout="@layout/layout_img"/>

代码:

ViewStub myViewStub=(ViewStub)findViewById(R.id.myViewStub);
myViewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
    @Override
    public void onInflate(ViewStub stub, View inflated) {
        Log.e("ViewStub","onInflate is invoking !!!");
    }
});
myViewStub.setVisibility(View.VISIBLE);

 

    好了,以上都是我在实践中的得到经验,分享给大家!

                                                                           

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值