Android源码系列(24) -- ViewStub

栏目: Android · 发布时间: 4年前

内容简介:ViewStub的父类是当类注释提供以下示例:

ViewStub的父类是 View ,但 ViewStub 本质是 不可见无尺寸运行时懒加载布局资源 的工具,自身不会展示到界面。

public final class ViewStub extends View

ViewStub 被设置为可见,或调用 inflate() 时开始布局资源填充。填充过程 ViewStub 会从所在的父布局中移除,并把懒加载填充的布局添加到相应位置,新填充的布局沿用预设给 ViewStubLayoutParams

类注释提供以下示例:

<ViewStub android:id="@+id/stub"
      android:inflatedId="@+id/subTree"
      android:layout="@layout/mySubTree"
      android:layout_width="120dip"
      android:layout_height="40dip" />

示例定义 ViewStub 的id为 R.id.stub ,填充布局资源为 R.layout.mySubTree ,填充后会指定 R.id.subTree 为新视图的id,以后就能用这个id引用布局。由于 ViewStub 加载布局后就被移除,再次填充就会出现异常。

// 填充之后给新视图设置的id
private int mInflatedId;
// 需要填充布局的id
private int mLayoutResource;

private WeakReference<View> mInflatedViewRef;

有多个重载构造方法,都会来到这里:

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

    // 从xml获取值
    final TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.ViewStub, defStyleAttr, defStyleRes);
    // 获取填充之后给新视图设置的id
    mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
    // 获取需要填充布局的id
    mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
    mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
    a.recycle();

    setVisibility(GONE);
    setWillNotDraw(true);
}

触发懒加载的方法,和 ViewStubsetVisibility() 调用后作用一样。

填充过程检查父布局合法性,其父布局为空可能存在两种情况:

  • ViewStub 通过代码构建,但没添加到父布局;
  • ViewStub 曾加载过,已从父布局移除;

这两种情况都不允许 ViewStub 继续填充,所以要抛出异常。

public View inflate() {
    // 获取ViewStub所在父布局
    final ViewParent viewParent = getParent();

    // 检查父布局是否为空,且父布局必须为ViewGroup,只有ViewGroup才能存放子视图
    if (viewParent != null && viewParent instanceof ViewGroup) {
        // 检查是否指定要加载布局的资源id
        if (mLayoutResource != 0) {
            final ViewGroup parent = (ViewGroup) viewParent;
            // 填充需要懒加载的视图
            final View view = inflateViewNoAdd(parent);
            // 替换视图
            replaceSelfWithView(view, parent);

            mInflatedViewRef = new WeakReference<>(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");
    }
}

懒加载视图经过LayoutInflater 填充为实例

private View inflateViewNoAdd(ViewGroup parent) {
    final LayoutInflater factory;
    if (mInflater != null) {
        factory = mInflater;
    } else {
        factory = LayoutInflater.from(mContext);
    }
    final View view = factory.inflate(mLayoutResource, parent, false);

    // 如果额外指定id,就把指定的id设置给新视图
    if (mInflatedId != NO_ID) {
        view.setId(mInflatedId);
    }
    return view;
}

获得填充后的布局实例,还要处理 ViewStub 本身,前后共四个步骤:

  • 获取 ViewStub 在父布局的位置,就一个索引值;

  • ViewStub 从所在父布局中移除,结束占位;
  • ViewStubLayoutParams 提供给新视图;
  • 新视图放在父布局 ViewStub 原来的位置,即第一步获取的索引值;
private void replaceSelfWithView(View view, ViewGroup parent) {
    // 获取ViewStub在父布局的位置
    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);
    }
}

LayoutInflater 实现里面,遇到 ViewStub 只是创建新实例,然后把当前使用的 LayoutInflater 实例提供给 ViewStub ,这样以后 ViewStub 加载视图时就使用同一个 LayoutInflater

// 创建类实例,args是自定义主题相关变量
final View view = constructor.newInstance(args);

// 类型是ViewStub
if (view instanceof ViewStub) {
    // 使用同一个Context给ViewStub设置LayoutInflater,日后View填充会使用
    final ViewStub viewStub = (ViewStub) view;
    viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
}
mConstructorArgs[0] = lastContext;
return view;

参考链接:

  • https://stackoverflow.com/questions/23783101/how-to-check-if-a-viewstub-is-already-inflated

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

九败一胜

九败一胜

李志刚 / 北京联合出版公司 / 2014-9-1 / 42.00元

所有的创业者都面临着很多问题,困惑不是个人的,是有共性的。 除了自身去摸索着石头走路,他们还可以通过学习,从那些在创业路上走得更远的创业者身上学到经验、教训。 这本书的主角——王兴,恰好就是一个很好的学习对象。出生于1979年的王兴,很早就创业了,2004他就开始和同学一块创业,2005年做出了校内网;2007年,他又做出了饭否网——这是中国最早的类似twitter的网站。 ......一起来看看 《九败一胜》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具