返回介绍

5.5 parseInclude 方法解析

发布于 2024-12-23 22:04:08 字数 4469 浏览 0 评论 0 收藏 0

/**
 * 解析 include 标签
 */
private void parseInclude(XmlPullParser parser, Context context, View parent,
    AttributeSet attrs) throws XmlPullParserException, IOException {
  int type;

	// 如果 include 标签的父 View 是 ViewGroup,则继续,否则抛出异常。
  if (parent instanceof ViewGroup) {
    // 使用主题包装。如果 include 中的 View 有自己的 attr 属性,则忽略。
    final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
    final int themeResId = ta.getResourceId(0, 0);
    final boolean hasThemeOverride = themeResId != 0;
    if (hasThemeOverride) {
      context = new ContextThemeWrapper(context, themeResId);
    }
    ta.recycle();

		// 获取 Layout 标签指向布局资源 id
    int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
    if (layout == 0) {
			// 获取 Layout 的 value 值
      final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
      if (value == null || value.length() <= 0) {
        throw new InflateException("You must specify a layout in the"
            + " include tag: <include layout=\"@layout/layoutID\" />");
      }

			// 尝试解析"?attr/name"成 id 资源
      layout = context.getResources().getIdentifier(value.substring(1), null, null);
    }

    // include 的布局可能引用了主题属性
    if (mTempValue == null) {
      mTempValue = new TypedValue();
    }
		// 尝试从主题中获取布局资源 id,如果有的话。
    if (layout != 0 && context.getTheme().resolveAttribute(layout, mTempValue, true)) {
      layout = mTempValue.resourceId;
    }
		
		// 如果还是无法找到布局 id,抛出异常。
    if (layout == 0) {
      final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
      throw new InflateException("You must specify a valid layout "
          + "reference. The layout ID " + value + " is not valid.");
    } else {
			
			// 获取 include 标签中布局的解析器
      final XmlResourceParser childParser = context.getResources().getLayout(layout);

      try {
        final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
				// 查找根节点
        while ((type = childParser.next()) != XmlPullParser.START_TAG &&
            type != XmlPullParser.END_DOCUMENT) {
          // Empty.
        }
				// 如果找不到根节点,抛出异常。
        if (type != XmlPullParser.START_TAG) {
          throw new InflateException(childParser.getPositionDescription() +
              ": No start tag found!");
        }
				
				// 获取标签名
        final String childName = childParser.getName();
				// 实例化 merge 标签
        if (TAG_MERGE.equals(childName)) {
					// <merge>标签不支持 android:theme,所以不需要其他处理
          rInflate(childParser, parent, context, childAttrs, false);
        } else {
					// 创建 View 实例化对象
          final View view = createViewFromTag(parent, childName,
              context, childAttrs, hasThemeOverride);
          final ViewGroup group = (ViewGroup) parent;

					// 获取 View 的 id 和可见性
          final TypedArray a = context.obtainStyledAttributes(
              attrs, R.styleable.Include);
          final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
          final int visibility = a.getInt(R.styleable.Include_visibility, -1);
          a.recycle();
					
					// 尝试加载<include />标签中的布局参数,如果父 View 无法生成布局参数(比如 include 标签下没有宽高参数是要抛出运行时异常的)
					// 捕获运行时异常,然后用引用的 Layout 的 attrs 来创建布局参数
          ViewGroup.LayoutParams params = null;
          try {
						// 使用 include 标签的 attrs 来生成布局参数
            params = group.generateLayoutParams(attrs);
          } catch (RuntimeException e) {
            // Ignore, just fail over to child attrs.
          }
          if (params == null) {
						// 如果 include 标签的 attrs 没有正确的生成布局参数,则使用 Layout 布局的 attrs 来生成布局参数
            params = group.generateLayoutParams(childAttrs);
          }
          view.setLayoutParams(params);

          // 实例化所有子 View
          rInflateChildren(childParser, view, childAttrs, true);

          if (id != View.NO_ID) {
            view.setId(id);
          }

          switch (visibility) {
            case 0:
              view.setVisibility(View.VISIBLE);
              break;
            case 1:
              view.setVisibility(View.INVISIBLE);
              break;
            case 2:
              view.setVisibility(View.GONE);
              break;
          }
					// 把布局实例化的 View 添加到父 View 上。
          group.addView(view);
        }
      } finally {
        childParser.close();
      }
    }
  } else {
    throw new InflateException("<include /> can only be used inside of a ViewGroup");
  }

  LayoutInflater.consumeChildElements(parser);
}

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

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

发布评论

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