返回介绍

子 View 的添加和删除

发布于 2024-12-23 22:09:58 字数 4845 浏览 0 评论 0 收藏 0

ViewGroup 添加子 View 的方法有多个,但是最终都会调 addView(child, index, params) 方法,并在这个方法内调用私有方法 addViewInner 来实现的。

我们前面知道了每个子 View 都需要有一个 LayoutParams 来告诉 ViewGroup 它布局的时候需要的一些参数,而当添加 View 的时候,View 没有 LayoutParams,或者我们调用了没有 LayoutParams 作为形参的 addView 方法的时候,ViewGroup 会调用自身的 generatedDefaultLayoutParams 来帮子 View 生成一个默认的 LayoutParams:

public void addView(View child, int index) {
    if (child == null) {
      throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
    }
    LayoutParams params = child.getLayoutParams();
    if (params == null) {
      // params 为空的情况,调用 generateDefaultLayoutParams 方法生成 LayoutParams
      params = generateDefaultLayoutParams();
      if (params == null) {
        throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
      }
    }
    addView(child, index, params);
  }

public void addView(View child, int width, int height) {
    // 调用这个方法的时候,会直接无视子 View 原来的 LayoutParams,
  		// 直接调用 generateDefaultLayoutParams 生成新的 LayoutParams
    final LayoutParams params = generateDefaultLayoutParams();
    params.width = width;
    params.height = height;
    addView(child, -1, params);
  }

然后是私有方法 addViewInner:

private void addViewInner(View child, int index, LayoutParams params,
      boolean preventRequestLayout) {
     ...

    // 子 View 的 parent 不为空,说明子 View 已经被添加到其他 ViewGroup 了。直接抛出异常。
    // 也就是说子 View 是不允许同时被添加到多个 ViewGroup 中的。这边挺好理解的,因为子 View 的布局相关参数都是唯一的,
    // 如果同时被添加到多个 ViewGroup,而 ViewGroup 的布局规则各不相同,会导致我们从某一个 ViewGroup 获取子 View 的时候,没法得到它正确的尺寸等相关信息
    if (child.getParent() != null) {
      throw new IllegalStateException("The specified child already has a parent. " +
          "You must call removeView() on the child's parent first.");
    }

    // 调用 checkLayoutParams 判断当前的 params 是不是我们需要的 LayoutParams,
  		// 很多继承自 ViewGroup 的布局都会用自己的 LayoutParams,并有独立的一些布局属性。
  		// 如果不是当前布局所需的 LayoutParams 则调用 generateLayoutParams 来转换
    if (!checkLayoutParams(params)) {
      params = generateLayoutParams(params);
    }

    // 给子 View 设置 LayoutParams
    if (preventRequestLayout) {
      child.mLayoutParams = params;
    } else {
      child.setLayoutParams(params);
    }

    if (index < 0) {
      index = mChildrenCount;
    }

    // 添加到子 View 的数组中
    addInArray(child, index);

    // 给子 View 设置 parent
    if (preventRequestLayout) {
      child.assignParent(this);
    } else {
      child.mParent = this;
    }

    // 判断并设置焦点
    if (child.hasFocus()) {
      requestChildFocus(child, child.findFocus());
    }

    // 判断 AttachInfo 是否为空,AttachInfo 不为空说明当前的 ViewGroup 是已经添加到 Window 上了。
  		// 调用子 View 的 dispatchAttachedToWindow。通知当前子 View 已经被添加到 Window
    AttachInfo ai = mAttachInfo;
    if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
      boolean lastKeepOn = ai.mKeepScreenOn;
      ai.mKeepScreenOn = false;
      child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
      if (ai.mKeepScreenOn) {
        needGlobalAttributesUpdate(true);
      }
      ai.mKeepScreenOn = lastKeepOn;
    }

    if (child.isLayoutDirectionInherited()) {
      child.resetRtlProperties();
    }

    dispatchViewAdded(child);
  
    // 判断是否需要通知子 View 状态改变(state 这边指按下、放开、选中等状态)
    if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
      mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
    }

    ...
  }

子 View 的移除则是最终调用了私有方法 removeViewInternal:

private void removeViewInternal(int index, View view) {
    if (mTransition != null) {
      mTransition.removeChild(this, view);
    }

    boolean clearChildFocus = false;
  
    // 判断并清除焦点
    if (view == mFocused) {
      view.unFocus(null);
      clearChildFocus = true;
    }

    view.clearAccessibilityFocus();

    // 取消相关的事件 Target
    cancelTouchTarget(view);
    cancelHoverTarget(view);

    // 当子 View 自身还有动画没有结束的时候,把子 View 添加到 disappearingChildren 列表中,
  		// 在 disappearingChildren 列表中的子 View 会在动画结束后被移除
    if (view.getAnimation() != null ||
        (mTransitioningViews != null && mTransitioningViews.contains(view))) {
      addDisappearingView(view);
    } else if (view.mAttachInfo != null) {
      // 子 View 自身没有动画在执行中,通知子 View 从 Window 中脱离
       view.dispatchDetachedFromWindow();
    }

    if (view.hasTransientState()) {
      childHasTransientStateChanged(view, false);
    }

    needGlobalAttributesUpdate(false);

    // 把子 View 移出子 View 数组
    removeFromArray(index);

    if (clearChildFocus) {
      clearChildFocus(view);
      if (!rootViewRequestFocus()) {
        notifyGlobalFocusCleared(this);
      }
    }

    dispatchViewRemoved(view);

    if (view.getVisibility() != View.GONE) {
      notifySubtreeAccessibilityStateChangedIfNeeded();
    }

    ...
  }

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

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

发布评论

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