返回介绍

ViewStub

发布于 2024-12-23 22:19:05 字数 3598 浏览 0 评论 0 收藏 0

ViewStub 是一个看不见的,没有大小,不占布局位置的 View,可以用来懒加载布局。当 ViewStub 变得可见或 inflate() 的时候,布局就会被加载(替换 ViewStub)。因此,ViewStub 一直存在于视图层次结构中直到调用了 setVisibility(int)inflate()

我们先来看看构造方法:

  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);
  }

接下来就看看关键的方法,ViewStub 很轻量,代码真的很少,很值得去看看具体实现,花不了多少时间。

  // 复写了 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();
      }
    }
  }

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");
    }
  }

好了,主要的都分析完了,知道原理之后就可以自己动手写一个加强版的 ViewStub 了,例如我以前写的一个 StateView

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文