本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。
前言
在开发中,对于 LayoutInflater 的 inflate() 方法,它的作用是把 xml 布局转换为对应的 View 对象,我们几乎天天在用;
今天我们就来分析讲解下;
一、什么是LayoutInflater?
LayoutInflater 的作用就是将XML布局文件实例化为相应的 View 对象,需要通过Activity.getLayoutInflater() 或 Context.getSystemService(Class) 来获取与当前Context已经关联且正确配置的标准LayoutInflater;
- @SystemService(Context.LAYOUT_INFLATER_SERVICE)
- public abstract class LayoutInflater {
- ...
- }
获取LayoutInflater
1、 View.inflate(...)
- public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
- LayoutInflater factory = LayoutInflater.from(context);
- return factory.inflate(resource, root);
- }
2、Activity#getLayoutInflater()
- Activity.class的源代码:
- public class Activity extends ....... {
- .........
- @NonNull
- public LayoutInflater getLayoutInflater() {
- return getWindow().getLayoutInflater();
- }
- .........
- @NonNull
- public LayoutInflater getLayoutInflater() {
- return getWindow().getLayoutInflater();
- }
- .........
- final void attach(.....){
- ......
- mWindow = new PhoneWindow(this, window, activityConfigCallback);
- .......
- }
- .........
- }
- PhoneWindow源码:
- public PhoneWindow(Context context) {
- super(context);
- mLayoutInflater = LayoutInflater.from(context);
- }
3、PhoneWindow#getLayoutInflater()
- private LayoutInflater mLayoutInflater;
- public PhoneWindow(Context context) {
- super(context);
- mLayoutInflater = LayoutInflater.from(context);
- }
- public LayoutInflater getLayoutInflater() {
- return mLayoutInflater;
- }
4、LayoutInflater#from(Context)
- @SystemService(Context.LAYOUT_INFLATER_SERVICE)
- public abstract class LayoutInflater {
- .....
- /**
- * Obtains the LayoutInflater from the given context.
- */
- 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;
- }
- .....
- }
二、源码分析
1、LayoutInflater#inflate(...)
调用inflate()进行布局解析
- public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
- final Resources res = getContext().getResources();
- 1. 解析预编译的布局
- View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
- if (view != null) {
- return view;
- }
- 2. 构造 XmlPull 解析器
- XmlResourceParser parser = res.getLayout(resource);
- try {
- 3. 执行解析
- return inflate(parser, root, attachToRoot);
- } finally {
- parser.close();
- }
- }
- tryInflatePrecompiled(...)是解析预编译的布局;
- 构造 XmlPull 解析器 XmlResourceParser
- 执行解析,是解析的主流程
2、inflate
- public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
- 1. 结果变量
- View result = root;
- 2. 最外层的标签
- final String name = parser.getName();
- 3. <merge>
- if (TAG_MERGE.equals(name)) {
- 3.1 异常
- if (root == null || !attachToRoot) {
- throw new InflateException("<merge /> can be used only with a valid "
- + "ViewGroup root and attachToRoot=true");
- }
- 3.2 递归执行解析
- rInflate(parser, root, inflaterContext, attrs, false);
- } else {
- 4.1 创建最外层 View
- final View temp = createViewFromTag(root, name, inflaterContext, attrs);
- ViewGroup.LayoutParams params = null;
- if (root != null) {
- 4.2 创建匹配的 LayoutParams
- params = root.generateLayoutParams(attrs);
- if (!attachToRoot) {
- 4.3 如果 attachToRoot 为 false,设置LayoutParams
- temp.setLayoutParams(params);
- }
- }
- 5. 以 temp 为 root,递归执行解析
- rInflateChildren(parser, temp, attrs, true);
- 6. attachToRoot 为 true,addView()
- if (root != null && attachToRoot) {
- root.addView(temp, params);
- }
- 7. root 为空 或者 attachToRoot 为 false,返回 temp
- if (root == null || !attachToRoot) {
- result = temp;
- }
- }
- return result;
- }
- -> 3.2
- void rInflate(XmlPullParser parser, View parent, Context context, AttributeSet attrs, boolean finishInflate) {
- while(parser 未结束) {
- if (TAG_INCLUDE.equals(name)) {
- 1) <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)) {
- 2) <merge>
- throw new InflateException("<merge /> must be the root element");
- } else {
- 3) 创建 View
- final View view = createViewFromTag(parent, name, context, attrs);
- final ViewGroup viewGroup = (ViewGroup) parent;
- final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
- 4) 递归
- rInflateChildren(parser, view, attrs, true);
- 5) 添加到视图树
- viewGroup.addView(view, params);
- }
- }
- }
- -> 5. 递归执行解析
- final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
- boolean finishInflate) throws XmlPullParserException, IOException {
- rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
- }
- 3、createViewFromTag
- createViewFromTag(),它负责由 <tag> 创建 View 对象
- View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
- boolean ignoreThemeAttr) {
- if (name.equals("view")) {
- name = attrs.getAttributeValue(null, "class");
- }
- // Apply a theme wrapper, if allowed and one is specified.
- 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();
- }
- if (name.equals(TAG_1995)) {
- // Let's party like it's 1995!
- return new BlinkLayout(context, attrs);
- }
- try {
- View view;
- if (mFactory2 != null) {
- // ① 有mFactory2,则调用mFactory2的onCreateView方法
- view = mFactory2.onCreateView(parent, name, context, attrs);
- } else if (mFactory != null) {
- // ② 有mFactory,则调用mFactory的onCreateView方法
- view = mFactory.onCreateView(name, context, attrs);
- } else {
- view = null;
- }
- if (view == null && mPrivateFactory != null) {
- // ③ 有mPrivateFactory,则调用mPrivateFactory的onCreateView方法
- view = mPrivateFactory.onCreateView(parent, name, context, attrs);
- }
- if (view == null) {
- // ④ 走到这步说明三个Factory都没有,则开始自己创建View
- final Object lastContext = mConstructorArgs[0];
- mConstructorArgs[0] = context;
- try {
- if (-1 == name.indexOf('.')) {
- // ⑤ 如果View的name中不包含 '.' 则说明是系统控件,会在接下来的调用链在name前面加上 'android.view.'
- view = onCreateView(parent, name, attrs);
- } else {
- // ⑥ 如果name中包含 '.' 则直接调用createView方法,onCreateView 后续也是调用了createView
- view = createView(name, null, attrs);
- }
- } finally {
- mConstructorArgs[0] = lastContext;
- }
- }
- return view;
- } catch (InflateException e) {
- throw e;
- }
- }
- createViewFromTag 方法比较简单,首先尝试通过 Factory 来创建View;
- 如果没有 Factory 的话则通过 createView 来创建View;
3、createView 方法解析
- public final View createView(String name, String prefix, AttributeSet attrs)
- throws ClassNotFoundException, InflateException {
- Constructor<? extends View> constructor = sConstructorMap.get(name);
- if (constructor != null && !verifyClassLoader(constructor)) {
- constructor = null;
- sConstructorMap.remove(name);
- }
- Class<? extends View> clazz = null;
- try {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
- if (constructor == null) {
- // Class not found in the cache, see if it's real, and try to add it
- clazz = mContext.getClassLoader().loadClass(
- prefix != null ? (prefix + name) : name).asSubclass(View.class);
- if (mFilter != null && clazz != null) {
- boolean allowed = mFilter.onLoadClass(clazz);
- if (!allowed) {
- failNotAllowed(name, prefix, attrs);
- }
- }
- // ① 反射获取这个View的构造器
- constructor = clazz.getConstructor(mConstructorSignature);
- 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) {
- // New class -- remember whether it is allowed
- clazz = mContext.getClassLoader().loadClass(
- prefix != null ? (prefix + name) : name).asSubclass(View.class);
- boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
- mFilterMap.put(name, allowed);
- if (!allowed) {
- failNotAllowed(name, prefix, attrs);
- }
- } else if (allowedState.equals(Boolean.FALSE)) {
- failNotAllowed(name, prefix, attrs);
- }
- }
- }
- 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;
- // ③ 使用反射创建 View 对象,这样一个 View 就被创建出来了
- final 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]));
- }
- mConstructorArgs[0] = lastContext;
- return view;
- } catch (ClassCastException e) {
- }
- }
createView 方法也比较简单,通过反射来创建的 View 对象;
4、 Factory2 接口
Factory2可以拦截实例化 View 的步骤,在 LayoutInflater 中有两个方法可以设置:
- 方法1:
- public void setFactory2(Factory2 factory) {
- if (mFactorySet) {
- 关注点:禁止重复设置
- throw new IllegalStateException("A factory has already been set on this LayoutInflater");
- }
- if (factory == null) {
- throw new NullPointerException("Given factory can not be null");
- }
- mFactorySet = true;
- if (mFactory == null) {
- mFactory = mFactory2 = factory;
- } else {
- mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
- }
- }
- 方法2 @hide
- public void setPrivateFactory(Factory2 factory) {
- if (mPrivateFactory == null) {
- mPrivateFactory = factory;
- } else {
- mPrivateFactory = new FactoryMerger(factory, factory, mPrivateFactory, mPrivateFactory);
- }
- }
使用 setFactory2() 和 setPrivateFactory() 可以设置 Factory2 接口(拦截器),其中同一个 LayoutInflater 的setFactory2()不能重复设置,setPrivateFactory() 是 hide 方法;
总结
- 通过 XML 的 Pull 解析方式获取 View 的标签;
- 通过标签以反射的方式来创建 View 对象;
- 如果是 ViewGroup 的话则会对子 View 遍历并重复以上步骤,然后 add 到父 View 中;
- 与之相关的几个方法:inflate ——》 rInflate ——》 createViewFromTag ——》 createView ;
- Factory2 是一个很实用的接口,需要掌握通过 setFactory2() 拦截布局解析的技巧;