返回介绍

5.3 onCreateView 和 createView 方法解析

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

onCreateView 有两个重载方法,最终调用的是 createView(String name, String prefix, AttributeSet attrs)

createView 主要做的操作有:

  1. 先通过 Filter,看是否过滤。
  2. 利用反射实例化 View 对象。

源码分析:

protected View onCreateView(View parent, String name, AttributeSet attrs)
    throws ClassNotFoundException {
  return onCreateView(name, attrs);
}

protected View onCreateView(String name, AttributeSet attrs)
    throws ClassNotFoundException {
	// 系统控件,前缀自动补上"android.view."
  return createView(name, "android.view.", attrs);
}

public final View createView(String name, String prefix, AttributeSet attrs)
    throws ClassNotFoundException, InflateException {
	// 通过以 View 的 name 为 key,查询构造函数的缓存 map 中时候已经有该 View 的构造函数。
  Constructor<? extends View> constructor = sConstructorMap.get(name);
  Class<? extends View> clazz = null;

  try {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);

		// 构造函数在缓存的 map 中没有,则尝试去创建并添加。
    if (constructor == null) {
			// 通过 类名去加载控件的字节码
      clazz = mContext.getClassLoader().loadClass(
          prefix != null ? (prefix + name) : name).asSubclass(View.class);
      //如果有自定义的过滤器并且加载到字节码,则通过过滤器判断是否允许加载该 View。
      if (mFilter != null && clazz != null) {
        boolean allowed = mFilter.onLoadClass(clazz);
        if (!allowed) {
					// 如果不允许则抛出异常。
          failNotAllowed(name, prefix, attrs);
        }
      }
			// 得到构造函数
      constructor = clazz.getConstructor(mConstructorSignature);
			constructor.setAccessible(true);
			// 缓存构造函数
      sConstructorMap.put(name, constructor);
    } else {
			// setFilter() 可能会在类的构造函数被添加到 map 之后,所以获取到 map 中的构造函数后还需要判断是否过滤。
			if (mFilter != null) {
         // 过滤的 map 中是否已经包含了此类名。
        Boolean allowedState = mFilterMap.get(name);
				// 当前类名没有被放到过滤的缓存 map 中
        if (allowedState == null) {
          // 重新加载类的字节码
          clazz = mContext.getClassLoader().loadClass(
              prefix != null ? (prefix + name) : name).asSubclass(View.class);
          // 重新通过过滤器判断是否过滤。
          boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
					// 把过滤结果放到过滤的缓存 map 中。
          mFilterMap.put(name, allowed);
          if (!allowed) {
						// 如果要过滤,则抛出异常。
            failNotAllowed(name, prefix, attrs);
          }
        } else if (allowedState.equals(Boolean.FALSE)) {
					// 缓存构造函数的 map 中已经保存了当前要实例化的 View 的构造函数并且是要过滤的,抛出异常。
          failNotAllowed(name, prefix, attrs);
        }
      }
    }

		// 实例化类的参数数组,0 是获取 LayoutInflater 传入的 Context,1 是 View 的属性
    Object[] args = mConstructorArgs;
    args[1] = attrs;
		// 通过构造函数实例化 View(看到此行就知道,为什么自定义 View 或者 ViewGroup 的时候,如果在布局中使用的话,必须重写两个参数的构造函数了。)
    final View view = constructor.newInstance(args);
		// 如果当前 View 是 ViewStub,则把布局填充器设置给它。(因为 ViewStub 在此刻并不会填充期子 View,而是等需要的时候由用户手动触发。)
    if (view instanceof ViewStub) {
      // 把当前 LayoutInflater 的克隆传递给 ViewStub,让 ViewStub 实例化的时候用,因为 ViewStub 只是在需要的时候才会实例化 View。
			final ViewStub viewStub = (ViewStub) view;
      viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
    }
    return view;

  } catch (NoSuchMethodException e) {
    InflateException ie = new InflateException(attrs.getPositionDescription()
        + ": Error inflating class "
        + (prefix != null ? (prefix + name) : name));
    ie.initCause(e);
    throw ie;

  } catch (ClassCastException e) {
    // If loaded class is not a View subclass
    InflateException ie = new InflateException(attrs.getPositionDescription()
        + ": Class is not a View "
        + (prefix != null ? (prefix + name) : name));
    ie.initCause(e);
    throw ie;
  } catch (ClassNotFoundException e) {
    // If loadClass fails, we should propagate the exception.
    throw e;
  } catch (Exception e) {
    InflateException ie = new InflateException(attrs.getPositionDescription()
        + ": Error inflating class "
        + (clazz == null ? "<unknown>" : clazz.getName()));
    ie.initCause(e);
    throw ie;
  } finally {
    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  }
}

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

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

发布评论

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