上一边文章中我们讲了setContentView的过程是如何拿到DecorView和contentParent的,这节我们将讲解拿到contentParent后,如何将R.layout.activity_main.xml的View创建出来
无论MainActivity是继承Activity还是继承AppCompatActivity它们创建View部分代码都是LayoutInflater的inflate方法创建View的
我们就从这里开始
LayoutInflater.from(mContext).inflate(resId, contentParent);
inflate通过多次调用就会进入下述inflate中
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {synchronized (mConstructorArgs) {...//省略代码try {....//省略代码//如果是merge标签的就必须有父布局attachToRoot必须为true,不然这里就会抛出异常,这个于merge标签的属性有关if (TAG_MERGE.equals(name)) {if (root == null || !attachToRoot) {throw new InflateException(" can be used only with a valid "+ "ViewGroup root and attachToRoot=true");}rInflate(parser, root, inflaterContext, attrs, false);} else {// Temp is the root view that was found in the xml//这创建rootView,这个rootView就是R.layout.activity_main.xml的根Viewfinal View temp = createViewFromTag(root, name, inflaterContext, attrs);...//创建子的View// Inflate all children under temp against its context.rInflateChildren(parser, temp, attrs, true);// We are supposed to attach all the views we found (int temp)// to root. Do that now.if (root != null && attachToRoot) {root.addView(temp, params);}// Decide whether to return the root that was passed in or the// top view found in xml.if (root == null || !attachToRoot) {result = temp;}}} catch (XmlPullParserException e) {...//省略代码} catch (Exception e) {...//省略代码} finally {...//省略代码}return result;}}
1.创建R.layout.activity_main.xml的根View
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
createViewFromTag()方法经过层层调用最终调用到下边代码
@UnsupportedAppUsageView createViewFromTag(View parent, String name, Context context, AttributeSet attrs,boolean ignoreThemeAttr) {...//省略代码try {View view = tryCreateView(parent, name, context, attrs);if (view == null) {..//省略代码try {if (-1 == name.indexOf('.')) {//如果sdk中的view空间将会走这个逻辑view = onCreateView(context, parent, name, attrs);} else {//自定义view将会走这里的逻辑view = createView(context, name, null, attrs);}} finally {mConstructorArgs[0] = lastContext;}}return view;} catch (InflateException e) {...//省略代码} catch (ClassNotFoundException e) {...//省略代码} catch (Exception e) {...//省略代码}}
先来看onCreateView(),经过层层调用会LayoutInflater.java中的
protected View onCreateView(View parent, String name, AttributeSet attrs)throws ClassNotFoundException {return onCreateView(name, attrs);}
这里需要注意的这两参的onCreateView(name, attrs);不是直调用LayoutInflater.java中的两参onCreateView方法,而是调用到PhoneLayoutInflater.java中的onCreateView(String name, AttributeSet attrs),因为LayoutInflater是一个抽象类,它是由PhoneLayoutInflater类实现的
@Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {for (String prefix : sClassPrefixList) {try {//调用到LayoutInflater中的三参方法View view = createView(name, prefix, attrs);if (view != null) {return view;}} catch (ClassNotFoundException e) {// In this case we want to let the base class take a crack// at it.}}//调用到LayoutInflater中的两参方法return super.onCreateView(name, attrs);}
private static final String[] sClassPrefixList = {"android.widget.","android.webkit.","android.app."};
这里的目的是通过sClassPrefixList 将空间的类名补全最后通过反射的方法创建View
从PhoneLayoutInflater的onCreateView()方法中看这里可以循环sClassPrefixList 数据来遍历补全空间的名称,当在sClassPrefixList 中没找到对应的空间的时候,就会返回父类的两参onCreateView(name, attrs);,
这里又回到了LayoutInflater
protected View onCreateView(String name, AttributeSet attrs)throws ClassNotFoundException {return createView(name, "android.view.", attrs);}
LayoutInflater的onCreateView(String name, AttributeSet attrs)方法也有一个"android.view."的前缀,相当于创建一个View的时候遍历了四次如果还没找到这个空间类,那么创建View就会出错
经过一系列的遍历调用最后会调用到LayoutInflater的createView(@NonNull Context viewContext, @NonNull String name, @Nullable String prefix, @Nullable AttributeSet attrs)方法通过反射来创建View了
public final View createView(@NonNull Context viewContext, @NonNull String name,@Nullable String prefix, @Nullable AttributeSet attrs)throws ClassNotFoundException, InflateException {...//省略代码try {...//省略代码if (constructor == null) {// Class not found in the cache, see if it's real, and try to add it //通过反射创建class的对象clazz = Class.forName(prefix != null ? (prefix + name) : name, false,mContext.getClassLoader()).asSubclass(View.class);...//省略代码//拿到构造对象constructor = clazz.getConstructor(mConstructorSignature);constructor.setAccessible(true);sConstructorMap.put(name, constructor);} else {...//省略代码}...//省略代码try {//创建Viewfinal View view = constructor.newInstance(args);if (view instanceof ViewStub) {// Use the same context when inflating ViewStub later.final ViewStub viewStub = (ViewStub) view;viewStub.setLayoutInflater(cloneInContext((Context) args[0]));}return view;} finally {...//省略代码}} catch (NoSuchMethodException e) {...//省略代码} catch (ClassCastException e) {...//省略代码} catch (ClassNotFoundException e) {...//省略代码} catch (Exception e) {...//省略代码} finally {...//省略代码}}
这里通过三步创建出View
(1) 通过反射创建class的对象
clazz = Class.forName(prefix != null ? (prefix + name) : name, false,mContext.getClassLoader()).asSubclass(View.class);
(2)拿到构造对象
constructor = clazz.getConstructor(mConstructorSignature);
(3)创建View
final View view = constructor.newInstance(args);
21.创建R.layout.activity_main.xml的子View
rInflateChildren(parser, temp, attrs, true);
*/void rInflate(XmlPullParser parser, View parent, Context context,AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {...//省略代码//这里会遍历整个R.layout.activity_main.xml创建其中的Viewwhile (((type = parser.next()) != XmlPullParser.END_TAG ||parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {...//省略代码if (TAG_REQUEST_FOCUS.equals(name)) {...//省略代码} else if (TAG_TAG.equals(name)) {...//省略代码} else if (TAG_INCLUDE.equals(name)) {...//省略代码} else if (TAG_MERGE.equals(name)) {throw new InflateException(" must be the root element");} else {//这里会递归的创建各个层级的View,createViewFromTag的逻辑和前边的都一样final View view = createViewFromTag(parent, name, context, attrs);final ViewGroup viewGroup = (ViewGroup) parent;final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);rInflateChildren(parser, view, attrs, true);viewGroup.addView(view, params);}}...//省略代码}
这里就会创建出R.layout.activity_main.xml中的所有View。
至此,整个R.layout.activity_main.xml布局中的所有View都被通过反射的方法创建出来,setContentView的工作结束,这里只是在DecorView中创建了View,DecorView还未被添加到Window中。