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

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

查看所有标签

猜你喜欢:

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

深入理解OpenCV

深入理解OpenCV

[巴西]Daniel Lelis Baggio / 刘波 / 机械工业出版社 / 2014-9 / 59

opencv是最常见的计算机视觉库之一,它提供了许多经过优化的复杂算法。本书对已掌握基本opencv技术同时想提高计算机视觉的实践经验的开发者来讲是一本非常好的书。每章都有一个单独的项目,其背景也在这些章节中进行了介绍。因此,读者可以依次学习这些项目,也可以直接跳到感兴趣的项目进行学习。 《深入理解opencv:实用计算机视觉项目解析》详细讲解9个实用的计算机视觉项目,通过本书的学习,读者可......一起来看看 《深入理解OpenCV》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

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

HEX HSV 互换工具