本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。
在开发中,对于 LayoutInflater 的 inflate() 方法,它的作用是把 xml 布局转换为对应的 View 对象,我们几乎天天在用;
今天我们就来分析讲解下;
LayoutInflater 的作用就是将XML布局文件实例化为相应的 View 对象,需要通过Activity.getLayoutInflater() 或 Context.getSystemService(Class) 来获取与当前Context已经关联且正确配置的标准LayoutInflater;
复制
@SystemService(Context.LAYOUT_INFLATER_SERVICE) public abstract class LayoutInflater { ... }
1.
2.
3.
4.
获取LayoutInflater
复制
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) { LayoutInflater factory = LayoutInflater.from(context); return factory.inflate(resource, root); }
1.
2.
3.
4.
复制
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); }
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
复制
private LayoutInflater mLayoutInflater; public PhoneWindow(Context context) { super(context); mLayoutInflater = LayoutInflater.from(context); } public LayoutInflater getLayoutInflater() { return mLayoutInflater; }
1.
2.
3.
4.
5.
6.
7.
8.
复制
@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.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
调用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(); } }
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
tryInflatePrecompiled(...)是解析预编译的布局;
构造 XmlPull 解析器 XmlResourceParser
执行解析,是解析的主流程
复制
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; } }
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
createViewFromTag 方法比较简单,首先尝试通过 Factory 来创建View;
如果没有 Factory 的话则通过 createView 来创建View;
复制
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) { } }
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
createView 方法也比较简单,通过反射来创建的 View 对象;
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); } }
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
复制
方法2 @hide public void setPrivateFactory(Factory2 factory) { if (mPrivateFactory == null) { mPrivateFactory = factory; } else { mPrivateFactory = new FactoryMerger(factory, factory, mPrivateFactory, mPrivateFactory); } }
1.
2.
3.
4.
5.
6.
7.
8.
使用 setFactory2() 和 setPrivateFactory() 可以设置 Factory2 接口(拦截器),其中同一个 LayoutInflater 的setFactory2()不能重复设置,setPrivateFactory() 是 hide 方法;
通过 XML 的 Pull 解析方式获取 View 的标签;
通过标签以反射的方式来创建 View 对象;
如果是 ViewGroup 的话则会对子 View 遍历并重复以上步骤,然后 add 到父 View 中;
与之相关的几个方法:inflate ——》 rInflate ——》 createViewFromTag ——》 createView ;
Factory2 是一个很实用的接口,需要掌握通过 setFactory2() 拦截布局解析的技巧;