LayoutInflater相关-布局XML文件转化为View

概述:平时开发,我们只需要在Activity的onCreate()方法中调用setContentView()方法就能实现页面的展示,同时也能调用findViewById()获取到对应的控件实例,那么layout的XML文件到底是怎么转化成View呢?

1、探索入口:setContentView()
public class MainActivity extends Activity{

@Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
    }
}

-->调用Activity的setContentView
public class Activity{

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        ...
    }
}


-->调用了Window类的setContentView
public class Window{
    public abstract void setContentView(@LayoutRes int layoutResID);
}


-->Window类的唯一实现类是PhoneWindow,最终是调用到了PhoneWindow的setContentView方法
public class PhoneWindow{
    private LayoutInflater mLayoutInflater;

    public PhoneWindow(Context context) {
        super(context);
        mLayoutInflater = LayoutInflater.from(context);
    }

    @Override
    public void setContentView(int layoutResID) {
          ...

          mLayoutInflater.inflate(layoutResID, mContentParent);

          ...
    }

}

分析:

  • Activity的setContentView()方法最终是调用了PhoneWindow中的setContentView();
  • PhoneWindow中的setContentView()是通过LayoutInflater的inflate(int resource, ViewGroup root)方法;
  • layoutResID的xml构建成View之后,会添加为mContentParent的子View(我们设置的布局并非页面的根布局,需要了解mContentParent请看mWindow.getDecorView);
2、LayoutInflater 的初始化
@SystemService(Context.LAYOUT_INFLATER_SERVICE)
public abstract class LayoutInflater {

    public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }

}

-->通过调用ContextImpl类的getSystemService()方法
class ContextImpl extends Context {

    @Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }

}

-->通过系统服务管理类来获取系统服务
/**
 * Manages all of the system services that can be returned by {@link Context#getSystemService}.
 * Used by {@link ContextImpl}.
 */
final class SystemServiceRegistry {

   private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
            new HashMap<String, ServiceFetcher<?>>();

    --> 所有的系统服务都是在静态代码块中注册的,
   static {
       ...
       registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
                new CachedServiceFetcher<LayoutInflater>() {
            @Override
            public LayoutInflater createService(ContextImpl ctx) {
                return new PhoneLayoutInflater(ctx.getOuterContext());
            }});

        ...
    }

    //把系统服务的名称和系统服务保存到常量Hashmap中
    private static <T> void registerService(String serviceName, Class<T> serviceClass,
            ServiceFetcher<T> serviceFetcher) {
        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
    }

    //通过服务名称获取对应的系统服务
    public static Object getSystemService(ContextImpl ctx, String name) {
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? fetcher.getService(ctx) : null;
    }

}

分析:

  • LayoutInflater实例的获取只能通过其内部的静态方法from()获取;
  • context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)实际上是通过SystemServiceRegistry的getSystemService()方法获取,调用一个常量的HashMap获取对应的服务;
  • 所有的系统服务都是在SystemServiceRegistry的静态代码块中注册保存的,这保证了服务不会被重复注册保存;
  • 所以LayoutInflater的实例获取其实是一个单例设计模式
3、mLayoutInflater.inflate(layoutResID, mContentParent)
public abstract class LayoutInflater {
   /**
     * Inflate a new view hierarchy from the specified xml resource. Throws
     * {@link InflateException} if there is an error.
     */
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
    }

    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();//获取Resources资源类
        ... 

        final XmlResourceParser parser = res.getLayout(resource);//获取布局文件对应xml解析器
        try {
            return inflate(parser, root, attachToRoot);//继续调用下一个方法
        } finally {
            parser.close();
        }
    }
}

这里只是获取Resources资源类,然后获取xml文件对应的xml解析器,再调用下一个inflate方法 。

3.1、开始解析布局xml文件

  public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            ...
            
            final AttributeSet attrs = Xml.asAttributeSet(parser);//获取xml的属性集合
            ...
            View result = root;

            try {
               //判断是否有开始结束标签
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }

                final String name = parser.getName();//获取标签名

                ...

                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        //如果父布局为空或者不需要添加到父布局之中,抛出异常,不直接解析merge为根标签的布局
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    //填充merge的xml布局,往下查看3.2
                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    //转换xml的根标签为temp,查看3.3
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    //配置根布局temp的布局参数
                    ViewGroup.LayoutParams params = null;
                    if (root != null) {
                        // Create layout params that match root, if supplied
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            // Set the layout params for temp if we are not
                            // attaching. (If we are, we use addView, below)
                            temp.setLayoutParams(params);
                        }
                    }

                    //填充跟标签的子view,往下查看3.2
                    rInflateChildren(parser, temp, attrs, true);

                    
                    //判断一下,把我们的布局根元素对应的view,添加为root的子view
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }

                    //把我们的布局根元素对应的view作为结果返回回去
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }

            } 
            ...

            finally {
                // Don't retain static reference on context.
                mConstructorArgs[0] = lastContext;
                mConstructorArgs[1] = null;

            }

            return result;
        }
    }

分析:

  1. 先从XmlPullParser获取到所有的属性集合,标签转化为view时,需要用到;
  2. 判断是否有开始结束标签,然后获取第一个标签,判断第一个标签是否为merge标签;
  3. 如果是merge标签,root不为空且要添加为root的子view时,调用rInflate方法解析其余标签;
  4. 如果第一个标签不是merge标签,调用createViewFromTag方法把此标签转化成view,然后调用rInflateChildren方法解析其余标签;然后判断是否需要把第一个标签添加为root的子view。

3.2、递归调用,遍历xml的所有标签


    -->填充parent的所有子view,
    final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
            boolean finishInflate) throws XmlPullParserException, IOException {
        rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
    }

    -->对parser进行循环获取,解析所有的标签
    void rInflate(XmlPullParser parser, View parent, Context context,
            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {

        final int depth = parser.getDepth();//获取标签嵌套的深度,即布局嵌套的深度
        int type;
        boolean pendingRequestFocus = false;

        //循环解析所有标签
        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {

            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            final String name = parser.getName();

            if (TAG_REQUEST_FOCUS.equals(name)) {//<requestFocus />标签
                pendingRequestFocus = true;
                consumeChildElements(parser);//
            } else if (TAG_TAG.equals(name)) {//<tag />标签
                parseViewTag(parser, parent, attrs);//给parent设置tag
            } else if (TAG_INCLUDE.equals(name)) {//<include />标签
                if (parser.getDepth() == 0) {
                    throw new InflateException("<include /> cannot be the root element");
                }
                parseInclude(parser, context, parent, attrs);
            } else if (TAG_MERGE.equals(name)) {
                throw new InflateException("<merge /> must be the root element");
            } else {
                final View view = createViewFromTag(parent, name, context, attrs);//把标签转换成View,往下看3.3
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflateChildren(parser, view, attrs, true);//对view包裹的子view进行填充
                viewGroup.addView(view, params);//添加为parent的子view
            }
        }

        ...
        if (finishInflate) {
            parent.onFinishInflate();//调用父View的onFinishInflate方法
        }
    }


    /** 
      * 循环parser当前深度下,所有嵌套的标签,但不进行任何操作;
      * 即跳过某个标签和这个标签包裹的所有子标签
      */
    final static void consumeChildElements(XmlPullParser parser)
            throws XmlPullParserException, IOException {
        int type;
        final int currentDepth = parser.getDepth();
        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
            // Empty
        }
    }

分析:

  1. rInflateChildren和rInflate是个递归方法,会互相调用;
  2. rInflate方法中循环所有所有的标签,对标签进行判断;
  3. 如果为<requestFocus />标签,那么跳过这个标签以及requestFocus嵌套下的所有子标签;
  4. 如果是<tag />标签,那么获取ViewTag主题,给parent设置tag,然后跳过;
  5. 如果为merge标签, 那么merge包裹的标签,会填充merge的子标签,最后也会重新调用rInflate的方法;
  6. 然后对普通的View标签调用createViewFromTag方法,转换成view;把这个view添加到父view中,然后调用rInflateChildren,递归填充此标签嵌套的子标签(如果存在子标签)。

3.3、把xml中的标签转换为View

public abstract class LayoutInflater {
.
    private Factory mFactory;
    private Factory2 mFactory2;
    private Factory2 mPrivateFactory;

    //我们可以实现LayoutInflater的Factory接口,再调用setFactory(),可以hook我们的布局View创建
    public interface Factory {
        public View onCreateView(String name, Context context, AttributeSet attrs);
    }

    -->中转方法,往下调用
    private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
        return createViewFromTag(parent, name, context, attrs, false);
    }

    View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
       ...

        // 给context加上主题属性
        if (!ignoreThemeAttr) {
            final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
            final int themeResId = ta.getResourceId(0, 0);
            if (themeResId != 0) {
                context = new ContextThemeWrapper(context, themeResId);
            }
            ta.recycle();
        }

       ...

        try {
            View view;
            //如果是继承Activity,而且没有实现Factory接口,mFactory和mFactory2都为空
            //如果是继承了AppCompatActivity,mFactory2不为空,onCreateView会在AppCompatDelegateImplV9中被调用
            if (mFactory2 != null) {
                view = mFactory2.onCreateView(parent, name, context, attrs);
            } else if (mFactory != null) {
                view = mFactory.onCreateView(name, context, attrs);
            } else {
                view = null;
            }

            //Activity赋值了mPrivateFactory,但是Activity类中的onCreateView实现为空,所以View依然为空
            if (view == null && mPrivateFactory != null) {
                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
            }

            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    //判断是否为自定义的View,有.的是自定义view,没有.的是系统的View
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(parent, name, attrs);//往下看3.4
                    } else {
                        view = createView(name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }

            return view;
        } 
        ...
    }

}

分析:

  1. 首先把context转换成ContextThemeWrapper,可以带上主题属性,传递给View的创建;
  2. 调用view的创建之前,会先判断mFactory,mFactory2和mPrivateFactory是否为空;
  3. 通常如果我们继承的是Activity类,mFactory和mFactory2是null,没有被赋值,而mPrivateFactory虽然在Activity中设置了,但是Factory得onCreateView方法没有被复写,所以最后View的创建会调用onCreateView()方法(3.4);
  4. 如果我们继承的是AppCompatActivity,那么会调用mFactory2接口的onCreateView方法,往下看4

3.4、利用反射创建name对应的View

public abstract class LayoutInflater {

    //用于传递给view的构造方法,保存和context和attr
    final Object[] mConstructorArgs = new Object[2];

    private Filter mFilter;//View实例化过滤器
    private HashMap<String, Boolean> mFilterMap;//过滤器记录

    //让我们定义是否允许这个Class对象的创建
    public interface Filter {
        boolean onLoadClass(Class clazz);
    }

    //用于给我们设置过滤器,可以过滤某些view的创建
    public void setFilter(Filter filter) {
        mFilter = filter;
        if (filter != null) {
            mFilterMap = new HashMap<String, Boolean>();
        }
    }

    //用来保存所有View的构造方法
    private static final HashMap<String, Constructor<? extends View>> sConstructorMap =
            new HashMap<String, Constructor<? extends View>>();

    -->中转方法,往下调用
    protected View onCreateView(View parent, String name, AttributeSet attrs)
            throws ClassNotFoundException {
        return onCreateView(name, attrs);
    }

    -->中转方法,拼接系统View的全类名,再往下调用
    protected View onCreateView(String name, AttributeSet attrs)
            throws ClassNotFoundException {
        return createView(name, "android.view.", attrs);//系统的View要拼接全类名
    }

    -->传入name,通过反射实例化对应的View
    public final View createView(String name, String prefix, AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        Constructor<? extends View> constructor = sConstructorMap.get(name);//获取构造方法
        //判断constructor的类加载器和LayoutInflater或者Context的类加载器是否为同一个
        if (constructor != null && !verifyClassLoader(constructor)) {
            constructor = null;
            sConstructorMap.remove(name);
        }
        Class<? extends View> clazz = null;

        try {

            if (constructor == null) {
                //获取对应View的Class对象
                clazz = mContext.getClassLoader().loadClass(
                        prefix != null ? (prefix + name) : name).asSubclass(View.class);

                if (mFilter != null && clazz != null) {//Filter接口不设置的话,mFilter为空
                    boolean allowed = mFilter.onLoadClass(clazz);
                    if (!allowed) {
                        failNotAllowed(name, prefix, attrs);
                    }
                }
                constructor = clazz.getConstructor(mConstructorSignature);//获取View的构造方法
                constructor.setAccessible(true);
                sConstructorMap.put(name, constructor);//缓存构造方法
            } else {
                // If we have a filter, apply it to cached constructor
                if (mFilter != null) {
                    // Have we seen this name before?
                    Boolean allowedState = mFilterMap.get(name);//获取过滤记录
                    if (allowedState == null) {
                        // 获取Class对象
                        clazz = mContext.getClassLoader().loadClass(
                                prefix != null ? (prefix + name) : name).asSubclass(View.class);
                        //如果Class对象不为空,且onLoadClass返回true,过滤这个view的创建
                        boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
                        mFilterMap.put(name, allowed);//记录是否过滤这个view的实例化
                        if (!allowed) {
                            failNotAllowed(name, prefix, attrs);//抛出InflateException异常
                        }
                    } else if (allowedState.equals(Boolean.FALSE)) {
                        failNotAllowed(name, prefix, attrs);
                    }
                }
            }
            
            //配置view构造方法的参数,
            Object lastContext = mConstructorArgs[0];
            if (mConstructorArgs[0] == null) {
                // Fill in the context if not already within inflation.
                mConstructorArgs[0] = mContext;
            }
            Object[] args = mConstructorArgs;
            args[1] = attrs;

            final View view = constructor.newInstance(args);//调用反射,实例化view
            if (view instanceof ViewStub) {
                // Use the same context when inflating ViewStub later.
                final ViewStub viewStub = (ViewStub) view;
                viewStub.setLayoutInflater(cloneInContext((Context) args[0]));//设置viewStub的LayoutInflater,用于后期填充viewStub对应的view
            }
            mConstructorArgs[0] = lastContext;
            return view;
            
        } 
        ...
    }
}

分析:

  • 实例化View是在createView方法里面;
  • 首先从构造方法的缓存里面获取View的构造方法,判断一下类加载器和LayoutInflater.class或者context的类加载器是否相同;
  • 如果View的构造方法为空,判断一下是否设置了这个View的实例化过滤,反射获取这个View的构造方法,并记录起来;
  • 如果View的构造方法不为空,判断是否设置了View过滤器,记录是否过滤这个view的实例化;
  • 最后调用constructor.newInstance()方法实例化View;
4、当我们继承AppCompatActivity,布局的填充会是另一种方式
-->如果继承的是AppCompatActivity,那么setContentView会调用到AppCompatActivity里面来
public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
        TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        final AppCompatDelegate delegate = getDelegate();
        //delegate是AppCompatDelegateImplV9的子类,最终会调用到AppCompatDelegateImplV9里面的installViewFactory方法 
        delegate.installViewFactory(); //这里会设置LayoutInflater的mFractory2,下面会讲到

        ...
    }
    
    -->调用到这里的setContentView
    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        //先获取AppCompatDelegate
        getDelegate().setContentView(layoutResID);
    }

    -->往下走getDelegate()
    @NonNull
    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }

}


-->调用到AppCompatDelegate里面的create
public abstract class AppCompatDelegate {

    public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
        return create(activity, activity.getWindow(), callback);
    }

    //下面的几个类都都是AppCompatDelegateImplV9的子类
    private static AppCompatDelegate create(Context context, Window window,
            AppCompatCallback callback) {
        if (Build.VERSION.SDK_INT >= 24) {
            return new AppCompatDelegateImplN(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 23) {
            return new AppCompatDelegateImplV23(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 14) {
            return new AppCompatDelegateImplV14(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 11) {
            return new AppCompatDelegateImplV11(context, window, callback);
        } else {
            return new AppCompatDelegateImplV9(context, window, callback);//最后会调用AppCompatDelegateImplV9的setConetentView()
        }
    }

}

当我们设置了继承AppCompatActivity,内部会通过getDelegate()方法,最终能获取到AppCompatDelegateImplV9获取其子类;

@RequiresApi(14)
class AppCompatDelegateImplV9 extends AppCompatDelegateImplBase
        implements MenuBuilder.Callback, LayoutInflater.Factory2 {


    -->这个方法在AppCompatActivity的onCreate()中被调用,设置LayoutInflater的mFactory2
    @Override
    public void installViewFactory() {
        LayoutInflater layoutInflater = LayoutInflater.from(mContext);
        if (layoutInflater.getFactory() == null) {
            LayoutInflaterCompat.setFactory2(layoutInflater, this);
        } else {
            if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImplV9)) {
                Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
                        + " so we can not install AppCompat's");
            }
        }
    }


    -->LayoutInflater.Factory2的接口方法,LayoutInflater中的mFactory2.onCreateView会回调进来这个方法
    @Override
    public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
        //这里会回调我们的Activity中的onCreateView方法,如果没有实现,那么返回null
        final View view = callActivityOnCreateView(parent, name, context, attrs);
        if (view != null) {
            return view;
        }

        //往下调用createView方法实例化View
        return createView(parent, name, context, attrs);
    }

    //回调Activity的onCreateView,如果我们有复写,返回onCreateView方法的结果
    View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) {
        // Let the Activity's LayoutInflater.Factory try and handle it
        if (mOriginalWindowCallback instanceof LayoutInflater.Factory) {
            final View result = ((LayoutInflater.Factory) mOriginalWindowCallback)
                    .onCreateView(name, context, attrs);
            if (result != null) {
                return result;
            }
        }
        return null;
    }


    //中转方法,用来加入一些判断
    @Override
    public View createView(View parent, final String name, @NonNull Context context,
            @NonNull AttributeSet attrs) {
        if (mAppCompatViewInflater == null) {
            mAppCompatViewInflater = new AppCompatViewInflater();
        }

        boolean inheritContext = false;
        if (IS_PRE_LOLLIPOP) {//如果sdk版本低于21,就是5.0以下的手机
            inheritContext = (attrs instanceof XmlPullParser)
                    // If we have a XmlPullParser, we can detect where we are in the layout
                    ? ((XmlPullParser) attrs).getDepth() > 1
                    // Otherwise we have to use the old heuristic
                    : shouldInheritContext((ViewParent) parent);
        }

        //通过AppCompatViewInflater的createView方法实例化View
        return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
                IS_PRE_LOLLIPOP, /* Only read android:theme pre-L (L+ handles this anyway) */
                true, /* Read read app:theme as a fallback at all times for legacy reasons */
                VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if enabled */
        );
    }
}

分析:

  • installViewFactory()方法在AppCompatActivity的onCreate()方法中被调用,也就是我们自己的Activity的onCreate()方法;
  • installViewFactory()方法中调用了LayoutInflaterCompat.setFactory2(layoutInflater, this),这里给LayoutInflater设置了mFractory2;
  • 也就是我们在调用setContentView()方法之前,就先设置好了LayoutInflater的mFractory2,那么LayoutInflater的createViewFromTag()方法会回调进来AppCompatDelegateImplV9的onCreateView(LayoutInflater.Fractory2接口方法)方法;
  • 经过一连串的调用,如果我们没有实现onCreateView方法,那么最终会调用AppCompatViewInflater的createView,来实例化View;
class AppCompatViewInflater {
    public final View createView(View parent, final String name, @NonNull Context context,
            @NonNull AttributeSet attrs, boolean inheritContext,
            boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
        final Context originalContext = context;

        //配置一下主题,上下文,略过
        ...

        View view = null;

        //根据标签名,返回android.support.v7.widget中的控件
        //如果是继承Activity,返回的是android.view中的控件
        switch (name) {
            case "TextView":
                view = new AppCompatTextView(context, attrs);
                break;
            case "ImageView":
                view = new AppCompatImageView(context, attrs);
                break;
            case "Button":
                view = new AppCompatButton(context, attrs);
                break;
            case "EditText":
                view = new AppCompatEditText(context, attrs);
                break;
            case "Spinner":
                view = new AppCompatSpinner(context, attrs);
                break;
            case "ImageButton":
                view = new AppCompatImageButton(context, attrs);
                break;
            case "CheckBox":
                view = new AppCompatCheckBox(context, attrs);
                break;
            case "RadioButton":
                view = new AppCompatRadioButton(context, attrs);
                break;
            case "CheckedTextView":
                view = new AppCompatCheckedTextView(context, attrs);
                break;
            case "AutoCompleteTextView":
                view = new AppCompatAutoCompleteTextView(context, attrs);
                break;
            case "MultiAutoCompleteTextView":
                view = new AppCompatMultiAutoCompleteTextView(context, attrs);
                break;
            case "RatingBar":
                view = new AppCompatRatingBar(context, attrs);
                break;
            case "SeekBar":
                view = new AppCompatSeekBar(context, attrs);
                break;
        }

        //如果view还是空,那么就走反射实例化View
        if (view == null && originalContext != context) {
            view = createViewFromTag(context, name, attrs);
        }

        if (view != null) {
            // If we have created a view, check its android:onClick
            checkOnClickListener(view, attrs);
        }

        return view;
    }
}

这里其实也很easy,判断一下标签名,直接新建一个android.support.v7.widge包中的控件;如果还是为空,就调用createViewFromTag利用反射实例化;

总结:
1、如果我们的MainActivity设置继承Activity,那么setContentView是会调用PhoneWindow的setContentView(),然后调用LayoutInflater.inflater()方法进行布局填充;
2、LayoutInflater中首先会用XmlPullParser对布局文件进行解析,然后循环遍历xml中的所有标签;
3、对标签的名字进行判断,处理一些特殊标签,然后普通的View标签调用createViewFromTag方法进行转换;
4、createViewFromTag方法会判断一下是否有设置Factory接口并且实现onCreateView方法来实例化View,最后如果view还是为空,则调用createView方法,通过标签的全类名反射进行View的实例化;
5、如果我们的MainActivity设置继承AppCompatActivity,AppCompatActivity的onCreate中会设置LayoutInflater的mFactory2,对View的实例化进行代理;
6、AppCompatDelegateImplV9类中继承了LayoutInflater.Factory2方法,并实现了onCreateView方法,那么View的实例化由AppCompatDelegateImplV9这里进行主导;
7、AppCompatDelegateImplV9会调用AppCompatViewInflater的createView方法实例化View,对标签名进行判断,如果是系统的控件名,返回android.support.v7.widge包中对应的控件,其他控件则用反射进行实例化。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容