Android源码系列(24) -- ViewStub

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

内容简介: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

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

查看所有标签

猜你喜欢:

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

Lua设计与实现

Lua设计与实现

codedump / 人民邮电出版社 / 2017-8 / 49.00元

本书首先介绍了Lua中的数据结构,比如通用数据是如何表示的、Lua的字符串以及表类型的实现原理,接着讨论了Lua虚拟机的实现,并且将其中重点的一些指令进行了分类讲解,最后讨论了垃圾回收、模块实现、热更新、协程等的实现原理。一起来看看 《Lua设计与实现》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具