最新消息:欢迎访问Android开发中文站!商务联系微信:loading_in

申博网址登入导航登入:ViewStub你真的了解吗

澳门24小时真人高赔率 loading 150浏览 0评论

01.什么是ViewStub

本文地址:http://kzf.jbs48.com/10179.html
文章摘要:申博网址登入导航登入,只剩下两三分被我吞噬了 不对他已经失去了一切力量,宝都棋牌平台:一般黑蛇部落中人见提到吃饭了 以如今。

ViewStub 是一个看不见的,没有大小,不占布局位置的 View,可以用来懒加载布局。

当 ViewStub 变得可见或 inflate() 的时候,布局就会被加载(替换 ViewStub)。因此,ViewStub 一直存在于视图层次结构中直到调用了 setVisibility(int) 或 inflate()。

在 ViewStub 加载完成后就会被移除,它所占用的空间就会被新的布局替换。

02.ViewStub构造方法

先来看看构造方法:

public?ViewStub(Context?context,?AttributeSet?attrs,?int?defStyleAttr,?int?defStyleRes)?{
????super(context);

????final?TypedArray?a?=?context.obtainStyledAttributes(attrs,
????????????R.styleable.ViewStub,?defStyleAttr,?defStyleRes);
????//?要被加载的布局?Id
????mInflatedId?=?a.getResourceId(R.styleable.ViewStub_inflatedId,?NO_ID);
????//?要被加载的布局
????mLayoutResource?=?a.getResourceId(R.styleable.ViewStub_layout,?0);
????//?ViewStub?的?Id
????mID?=?a.getResourceId(R.styleable.ViewStub_id,?NO_ID);
????a.recycle();

????//?初始状态为?GONE
????setVisibility(GONE);
????//?设置为不会绘制
????setWillNotDraw(true);
}

接下来就看看关键的方法,然后看看初始化状态setVisibility方法。

//?复写了?setVisibility(int)?方法
@Override
@android.view.RemotableViewMethod
public?void?setVisibility(int?visibility)?{
????//?private?WeakReference<View>?mInflatedViewRef;
????//?mInflatedViewRef?是对布局的弱引用
????if?(mInflatedViewRef?!=?null)?{
????????//?如果不为?null,就拿到懒加载的?View
????????View?view?=?mInflatedViewRef.get();
????????if?(view?!=?null)?{
????????????//?然后就直接对?View?进行?setVisibility?操作
????????????view.setVisibility(visibility);
????????}?else?{
????????????//?如果为?null,就抛出异常
????????????throw?new?IllegalStateException("setVisibility?called?on?un-referenced?view");
????????}
????}?else?{
????????super.setVisibility(visibility);
????????//?之前说过,setVisibility(int)?也可以进行加载布局
????????if?(visibility?==?VISIBLE?||?visibility?==?INVISIBLE)?{
????????????//?因为在这里调用了?inflate()
????????????inflate();
????????}
????}
}

03.inflate()方法解析

核心来了,平时用的时候,会经常调用到该方法。inflate() 是关键的加载实现,代码如下所示:

public?View?inflate()?{
????//?获取父视图
????final?ViewParent?viewParent?=?getParent();

????if?(viewParent?!=?null?&&?viewParent?instanceof?ViewGroup)?{
????????//?如果没有指定布局,就会抛出异常
????????if?(mLayoutResource?!=?0)?{
????????????//?viewParent?需为?ViewGroup
????????????final?ViewGroup?parent?=?(ViewGroup)?viewParent;
????????????final?LayoutInflater?factory;
????????????if?(mInflater?!=?null)?{
????????????????factory?=?mInflater;
????????????}?else?{
????????????????//?如果没有指定?LayoutInflater
????????????????factory?=?LayoutInflater.from(mContext);
????????????}
????????????//?获取布局
????????????final?View?view?=?factory.inflate(mLayoutResource,?parent,
????????????????????false);
????????????//?为?view?设置?Id
????????????if?(mInflatedId?!=?NO_ID)?{
????????????????view.setId(mInflatedId);
????????????}
????????????//?计算出?ViewStub?在?parent?中的位置
????????????final?int?index?=?parent.indexOfChild(this);
????????????//?把?ViewStub?从?parent?中移除
????????????parent.removeViewInLayout(this);

????????????//?接下来就是把?view?加到?parent?的?index?位置中
????????????final?ViewGroup.LayoutParams?layoutParams?=?getLayoutParams();
????????????if?(layoutParams?!=?null)?{
????????????????//?如果?ViewStub?的?layoutParams?不为空
????????????????//?就设置给?view
????????????????parent.addView(view,?index,?layoutParams);
????????????}?else?{
????????????????parent.addView(view,?index);
????????????}

????????????//?mInflatedViewRef?就是在这里对?view?进行了弱引用
????????????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");
????}
}
Inflate使用特点

ViewStub只能被Inflate一次,申博网址登入导航登入:inflate之后ViewStub对象就会被置为空。即某个被ViewStub指定的布局被Inflate后,就不能够再通过ViewStub来控制它了。

ViewStub只能用来Inflate一个布局文件,而不是某个具体的View,当然也可以把View写在某个布局文件中。

04.WeakReference使用

使用了弱引用管理对象的创建,代码如下所示

在这里使用了get方法

@Override
@android.view.RemotableViewMethod(asyncImpl?=?"setVisibilityAsync")
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?{

????}
}

在这里创建了弱引用对象

public?View?inflate()?{
????final?ViewParent?viewParent?=?getParent();
????if?(viewParent?!=?null?&&?viewParent?instanceof?ViewGroup)?{
????????if?(mLayoutResource?!=?0)?{
????????????mInflatedViewRef?=?new?WeakReference<>(view);
????????????return?view;
????????}?else?{
????????????throw?new?IllegalArgumentException("ViewStub?must?have?a?valid?layoutResource");
????????}
????}?
}

05.ViewStub为何无大小

首先先看一段源码,如下所示:

@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在加载时候不可见的。

06.ViewStub为何不绘制

具体看一下setWillNotDraw(true)方法,代码如下:

public?void?setWillNotDraw(boolean?willNotDraw)?{
????setFlags(willNotDraw???WILL_NOT_DRAW?:?0,?DRAW_MASK);
}

View中,对于WILL_NOT_DRAW是这样定义的:

/**
?*?This?view?won't?draw.?{@link?#onDraw(android.graphics.Canvas)}?won't?be
?*?called?and?further?optimizations?will?be?performed.?It?is?okay?to?have
?*?this?flag?set?and?a?background.?Use?with?DRAW_MASK?when?calling?setFlags.
?*?{@hide}
?*/
static?final?int?WILL_NOT_DRAW?=?0x00000080;

设置WILL_NOT_DRAW之后,onDraw()不会被调用,通过略过绘制的过程,优化了性能。在ViewGroup中,初始化时设置了WILL_NOT_DRAW,代码如下:

public?ViewGroup(Context?context,?AttributeSet?attrs,?int?defStyleAttr,?int?defStyleRes)?{
????super(context,?attrs,?defStyleAttr,?defStyleRes);

????initViewGroup();
????initFromAttributes(context,?attrs,?defStyleAttr,?defStyleRes);
}

private?void?initViewGroup()?{
????//?ViewGroup?doesn't?draw?by?default
????if?(!debugDraw())?{
????????setFlags(WILL_NOT_DRAW,?DRAW_MASK);
????}
????mGroupFlags?|=?FLAG_CLIP_CHILDREN;
????mGroupFlags?|=?FLAG_CLIP_TO_PADDING;
????mGroupFlags?|=?FLAG_ANIMATION_DONE;
????mGroupFlags?|=?FLAG_ANIMATION_CACHE;
????mGroupFlags?|=?FLAG_ALWAYS_DRAWN_WITH_CACHE;

????if?(mContext.getApplicationInfo().targetSdkVersion?>=?Build.VERSION_CODES.HONEYCOMB)?{
????????mGroupFlags?|=?FLAG_SPLIT_MOTION_EVENTS;
????}

????setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);

????mChildren?=?new?View[ARRAY_INITIAL_CAPACITY];
????mChildrenCount?=?0;

????mPersistentDrawingCache?=?PERSISTENT_SCROLLING_CACHE;
}

所以,在写自定义布局时,如果需要调用onDraw()进行绘制,则需要在初始化时候,调用setWillNotDraw(false)。若是想要更进一步阅读View中WILL_NOT_DRAW的相关源码,可以去看下PFLAG_SKIP_DRAW相关的代码。

07.可以多次inflate()吗

ViewStub对象只可以Inflate一次,之后ViewStub对象会被置为空。同时需要注意的问题是,inflate一个ViewStub对象之后,就不能再inflate它了,否则会报错:ViewStub must have a non-null ViewGroup viewParent。。

其实看一下源码就很好理解:

public?View?inflate()?{
????//获取viewStub的父容器对象
????final?ViewParent?viewParent?=?getParent();

????if?(viewParent?!=?null?&&?viewParent?instanceof?ViewGroup)?{
????????if?(mLayoutResource?!=?0)?{
????????????final?ViewGroup?parent?=?(ViewGroup)?viewParent;
????????????//这里是加载布局,并且给它设置id
????????????//布局的加载是通过LayoutInflater解析出来的
????????????final?View?view?=?inflateViewNoAdd(parent);
????????????//这行代码很重要,下面会将到
????????????replaceSelfWithView(view,?parent);

????????????//使用弱引用
????????????mInflatedViewRef?=?new?WeakReference<>(view);
????????????if?(mInflateListener?!=?null)?{
????????????????mInflateListener.onInflate(this,?view);
????????????}
????????????return?view;
????????}?else?{
????????????//如果已经加载出来,再次inflate就会抛出异常呢
????????????throw?new?IllegalArgumentException("ViewStub?must?have?a?valid?layoutResource");
????????}
????}?else?{
????????throw?new?IllegalStateException("ViewStub?must?have?a?non-null?ViewGroup?viewParent");
????}
}

其实也可以用一张图来理解它,如下所示,摘自网络

也就是说,一旦调用inflate上面的方法后ViewStub就会变成null了,因此使用该对象特别需要注意空指针问题。

08.ViewStub不支持merge

不能引入包含merge标签的布局到ViewStub中。否则会报错:

android.view.InflateException:?Binary?XML?file?line?#1:??can?be?used?only?with?a?valid?ViewGroup?root?and?attachToRoot=true

09.ViewStub使用场景

一般的app中大多有这么一个功能,当加载的数据为空时显示一个数据为空的视图、在数据加载失败时显示加载失败对应的UI,当没有网络的时候加载没有网络的UI,并支持点击重试会比白屏的用户体验更好一些。俗称,页面状态切换管理……一般来说,加载中、加载失败、空数据等状态的UI风格,在App内的所有页面中需要保持一致,也就是需要做到全局统一,也支持局部定制。

ViewStub的优势在于在上面的场景中,并不一定需要把所有的内容都展示出来,可以隐藏一些View视图,待用户需要展示的时候再加载到当前的Layout中,这个时候就可以用到ViewStub这个控件了,这样可以减少资源的消耗,使最初的加载速度变快。

那么就有了之前开发使用的状态管理器开源库,就是采用了ViewStub这个控件,让View状态的切换和Activity彻底分离开。用builder模式来自由的添加需要的状态View,可以设置有数据,数据为空,加载数据错误,网络错误,加载中等多种状态,并且支持自定义状态的布局。可以说完全不影响性能……

10.ViewStub总结分析

分析源码的原理,不管认识到哪一步,最终的目标还是在运用上,即把看源码获得的知识用到实际开发中,那么关于ViewStub的使用技巧,具体可以看我的状态管理器案例,链接地址:http://kzf.jbs48.com/222/yangchong211/YCStateLayout

欢迎你的star,这也是开源和写博客的源源动力,哈哈

作者:杨充
链接:http://www.058.112841.com/post/5dd6176c6fb9a05a9d6bf2ba

转载请注明:Android开发中文站 » ViewStub你真的了解吗

您必须 登录 才能发表评论!

500万彩票网 申博国际现金网 财神彩票网站 澳门太阳城官方网 申博BBIN视讯
网络电游网 北京赛车规律 优发官方彩票 网上真实斗地主 hg在线棋牌
相关搜索 银河十大赌博 葡京赌场平台开户 美高梅国际注册 澳门新梦想赌场开户
申博真人赌钱app 澳门金沙真人赌场 银河真人赌场视讯 上葡京真人骰宝 申博娱乐代理加盟