Android 简单解读了ViewStub的代码和注释,其他的不想多说

ViewStub应用的场景比较少,以前用过几次。时间一长又忘记了,很是苦恼。

于是把ViewStub的源码过了一遍,详细说明了每行代码的含义和目的,翻译了源码中的注释。

自己动眼看看吧,你懂得。再多说就画蛇添足了。

道士,请:



package android.view;

import android.annotation.IdRes;

import android.annotation.LayoutRes;

import android.content.Context;

import android.content.res.TypedArray;

import android.graphics.Canvas;

import android.util.AttributeSet;

import android.widget.RemoteViews.RemoteView;

import com.android.internal.R;

import java.lang.ref.WeakReference;

/**

* ViewStub是一个不可见的,0 size的View,它可以在运行时加载一个布局,即懒加载。

* 当ViewStub设置为可见(visible),或者调用了ViewStub的inflate()方法时,

* 其android:layout属性所关联的layout文件才会被加载和生成view对象。

* 此时,当前ViewStub所在的位置会被其加载的子布局霸占替换;并且当前ViewStub对象会从其父容器中移除。

* 这也就是为什么ViewStub只能inflate()一次的原因。inflate一次后,ViewStub对象都没有了,还怎么inflate()第二次?

*

* 下面这句话注意了:

* 被ViewStub加载的View,替换了ViewStub的存在,当然是添加到了ViewStub的父容器中,即原ViewStub的爸爸成了ViewStub加载的View的爸爸。

* 并且在ViewStub上设置的layout参数,会转移给ViewStub加载的View身上。

* 同样的,你也可以在ViewStub的android:inflatedId属性上设置id,当其加载布局时,会直接把id重新设置到加载的布局根上。

* 如果没设置android:inflatedId属性,那待加载的布局根还是用以前的id属性,没有拉到。

*

* 举个栗子:

*

*

*

*android:inflatedId="@+id/这个id会在运行时加载中,设置到下面aaaaa布局的根属性上"

*android:layout="@layout/aaaaa,即稍后会加载且替换当前ViewStub的布局"

*android:layout_width="120dp"

*android:layout_height="40dp"/>

*

*

* 如上面的代码,当aaaaa布局加载后,你可以使用inflatedId属性值(即id值)找到aaaaa对象

* 或者用aaaaa内部的根布局id找到aaaaa对象。

*

* 下面ViewStub的代码:

*

*     ViewStubstub= (ViewStub) findViewById(R.id.viewStub的id);

*     Viewinflated=stub.inflate();

*

*

* 当inflate()方法被调用后,当前的ViewStub将会被加载的布局替换,并且返回值为加载的布局对象。

*

* 注意下面两个属性的定义:

* @attr ref android.R.styleable#ViewStub_inflatedId 给待加载的布局设置id,也可以不设置,用布局内部设置的id值

* @attr ref android.R.styleable#ViewStub_layout 待加载的布局

*/

@RemoteView

public final class ViewStub extends View {

private int mInflatedId;

private int mLayoutResource;

private WeakReferencemInflatedViewRef;

private LayoutInflater mInflater;

private OnInflateListener mInflateListener;

public ViewStub(Context context) {

this(context, 0);

}

/**

* 使用给定的待加载的布局文件创建一个ViewStub对象。

*

* @param context The application's environment.

* @param layoutResource The reference to a layout resource that will be inflated.

*/

public ViewStub(Context context, @LayoutRes int layoutResource) {

this(context, null);

mLayoutResource=layoutResource;

}

public ViewStub(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public ViewStub(Context context, AttributeSet attrs, int defStyleAttr) {

this(context, attrs, defStyleAttr, 0);

}

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

super(context);

final TypedArraya=context.obtainStyledAttributes(attrs,

R.styleable.ViewStub, defStyleAttr, defStyleRes);

mInflatedId=a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);

mLayoutResource=a.getResourceId(R.styleable.ViewStub_layout, 0);

mID=a.getResourceId(R.styleable.ViewStub_id, NO_ID);

a.recycle();

//注意,ViewStub永远是隐藏的,不会展示任何UI信息,且不占位

setVisibility(GONE);

setWillNotDraw(true);

}

/**

* 返回待加载的布局的id,如果没有设置(即保持布局内部根属性上设置的id)则返回NO_ID

*

* @return A positive integer used to identify the inflated view or

*         {@link #NO_ID} if the inflated view should keep its id.

*

* @see #setInflatedId(int)

* @attr ref android.R.styleable#ViewStub_inflatedId

*/

@IdRes

public int getInflatedId() {

return mInflatedId;

}

/**

* Defines the id taken by the inflated view. If the inflated id is

* {@link View#NO_ID}, the inflated view keeps its original id.

*

* @param inflatedId A positive integer used to identify the inflated view or

*                   {@link #NO_ID} if the inflated view should keep its id.

*

* @see #getInflatedId()

* @attr ref android.R.styleable#ViewStub_inflatedId

*/

@android.view.RemotableViewMethod

public void setInflatedId(@IdRes int inflatedId) {

mInflatedId=inflatedId;

}

/**

* 获取待加载的布局资源

*

* @return The layout resource identifier used to inflate the new View.

*

* @see #setLayoutResource(int)

* @see #setVisibility(int)

* @see #inflate()

* @attr ref android.R.styleable#ViewStub_layout

*/

@LayoutRes

public int getLayoutResource() {

return mLayoutResource;

}

/**

* 代码形式设置待加载的布局资源

*

* @param layoutResource A valid layout resource identifier (different from 0.)

*

* @see #getLayoutResource()

* @see #setVisibility(int)

* @see #inflate()

* @attr ref android.R.styleable#ViewStub_layout

*/

@android.view.RemotableViewMethod

public void setLayoutResource(@LayoutRes int layoutResource) {

mLayoutResource=layoutResource;

}

/**

* Set {@link LayoutInflater} to use in {@link #inflate()}, or {@code null}

* to use the default.

*/

public void setLayoutInflater(LayoutInflater inflater) {

mInflater=inflater;

}

/**

* Get current {@link LayoutInflater} used in {@link #inflate()}.

*/

public LayoutInflater getLayoutInflater() {

return mInflater;

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

//设置当前ViewStub的宽高都为0,即不可见了

setMeasuredDimension(0, 0);

}

@Override

public void draw(Canvas canvas) {

//无绘制

}

@Override

protected void dispatchDraw(Canvas canvas) {

//也不调用子孩子的绘制

}

/**

* When visibility is set to {@link #VISIBLE} or {@link #INVISIBLE},

* {@link #inflate()} is invoked and this StubbedView is replaced in its parent

* by the inflated layout resource. After that calls to this function are passed

* through to the inflated view.

*

* @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.

*

* @see #inflate()

*/

@Override

@android.view.RemotableViewMethod

public void setVisibility(int visibility) {

//判断mInflatedViewRef对象是否为null,即判断是否调用过下面的inflate()函数

if (mInflatedViewRef != null) {

//已经加载过view,直接获取。

Viewview=mInflatedViewRef.get();

if (view != null) {

//直接设置view的可见性即可,此时跟ViewStub没什么关系了。ViewStub已经从父容器中移除了

view.setVisibility(visibility);

} else {

throw new IllegalStateException("setVisibility called on un-referenced view");

}

} else {

super.setVisibility(visibility);

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

//如果设置了ViewStub为可见,或者占位的话,则触发下面的inflate()函数

//注意,有了mInflatedViewRef的判断,inflate()函数是不会调用第二次的。

//当调用了一次inflate()函数后,当前ViewStub对象没有父容器了,然而在调用inflate()会报异常的。

inflate();

}

}

}

/**

* Inflates the layout resource identified by {@link #getLayoutResource()}

* and replaces this StubbedView in its parent by the inflated layout resource.

*

* @return The inflated layout resource.

*

*/

public View inflate() {

//获取到当前ViewStub对象的父容器

final ViewParentviewParent=getParent();

//判断父容器是否为null

if (viewParent != null && viewParent instanceof ViewGroup) {

//判断是否给ViewStub指定了android:layout=""属性,即待加载的布局

if (mLayoutResource != 0) {

final ViewGroupparent= (ViewGroup) viewParent;

final LayoutInflater factory;

if (mInflater != null) {

factory=mInflater;

} else {

factory=LayoutInflater.from(mContext);

}

//加载指定的资源文件,注意三个参数的含义

//可参考:http://blog.csdn.net/fesdgasdgasdg/article/details/72870280

final Viewview=factory.inflate(mLayoutResource, parent,

false);

//如果设置了android:inflatedId=""属性值,则把设置的id值赋给被加载的布局

if (mInflatedId != NO_ID) {

view.setId(mInflatedId);

}

//记录当前ViiewStub在父容器中的位置

final intindex=parent.indexOfChild(this);

//把当前ViewStub对象从父容器中移除

parent.removeViewInLayout(this);

//获取当前ViewStub的layoutParams参数

final ViewGroup.LayoutParamslayoutParams=getLayoutParams();

if (layoutParams != null) {

//连同ViewStub的layoutParams参数一起,把加载获得到的View设置到父容器相应的位置,代替了ViewStub的存在

//注意layoutParams参数包含哪些值

parent.addView(view, index, layoutParams);

} else {

parent.addView(view, index);

}

//创建加载布局的弱引用,在上面setVisibility()函数中会用到

mInflatedViewRef=newWeakReference(view);

if (mInflateListener != null) {

//布局加载成功,并且成功替换了ViewStub的位置,和拥有了ViewStub的资源。于是执行回调通知调用者

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成功的加载了布局资源后,会回调此接口进行通知

*

* @param inflateListener The OnInflateListener to notify of successful inflation.

*

* @see android.view.ViewStub.OnInflateListener

*/

public void setOnInflateListener(OnInflateListener inflateListener) {

mInflateListener=inflateListener;

}

/**

* 资源文件成功被ViewStub加载后的监听器

*

* @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener)

*/

public static interface OnInflateListener {

/**

* Invoked after a ViewStub successfully inflated its layout resource.

* This method is invoked after the inflated view was added to the

* hierarchy but before the layout pass.

*

* @param stub The ViewStub that initiated the inflation.

* @param inflated The inflated View.

*/

void onInflate(ViewStub stub, View inflated);

}

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容