目录
前言
简单记录一下Android中LayoutInflater中inflate()的使用以及源码的简单跟踪。
记录于此,方便自己查阅。
正文
LayoutInflater初始化
对于LayoutInflater,我们并不陌生,经常使用。
- //方法1
- LayoutInflater inflater_1 = getLayoutInflater(); // 调用Activity的getLayoutInflater()
- //方法2
- LayoutInflater inflater_2 = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- //方法3
- LayoutInflater inflater_3 = LayoutInflater.from(mContext);
尤其是方法3,写ListView,RecyclerView等的适配器时加载Item_layout.xml时。
上面初始化LayoutInflater效果都一样,下面简单跟踪一下。
Activity.java
- frameworks\base\core\java\android\app\Activity.java
- @NonNull
- public LayoutInflater getLayoutInflater() {
- return getWindow().getLayoutInflater();
- }
- public Window getWindow() {
- return mWindow;
- }
这里的mWindow是PhoneWindow对象。
- final void attach(Context context, ActivityThread aThread,
- Instrumentation instr, IBinder token, int ident,
- Application application, Intent intent, ActivityInfo info,
- CharSequence title, Activity parent, String id,
- NonConfigurationInstances lastNonConfigurationInstances,
- Configuration config, String referrer, IVoiceInteractor voiceInteractor,
- Window window, ActivityConfigCallback activityConfigCallback) {
- attachBaseContext(context);
- mFragments.attachHost(null /*parent*/);
- //初始化mWindow
- mWindow = new PhoneWindow(this, window, activityConfigCallback);
- //略
- }
PhoneWindow.java
- public PhoneWindow(Context context) {
- super(context);
- mLayoutInflater = LayoutInflater.from(context);
- }
-
- public PhoneWindow(Context context, Window preservedWindow,
- ActivityConfigCallback activityConfigCallback) {
- this(context);
- //略
- }
- @Override
- public LayoutInflater getLayoutInflater() {
- return mLayoutInflater;
- }
从这里可以看到,方法的获取mLayoutInflater跟方法3一样,只不是是在PhoneWindow中初始化。
接着看
- mLayoutInflater = LayoutInflater.from(context);
LayoutInflater.java
- \frameworks\base\core\java\android\view\LayoutInflater.java
- 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;
- }
哈哈哈,这个不是方法2么。
小结
方法1—》方法3 —》方法2,(—》表示调用关系)
三种方法都是一样的效果,最终都是通过getSystemService()获取
inflate()介绍
在LayoutInflater中inflate()有多个,参数不一样。一共有三个,不过最终都调用到第三个。
第一个
- public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
- //如果root不为null,就attachToRoot
- return inflate(parser, root, root != null);
- }
第二个
- public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
- final Resources res = getContext().getResources();
- final XmlResourceParser parser = res.getLayout(resource);
- try {
- //是否attachToRoot,要看参数attachToRoot
- return inflate(parser, root, attachToRoot);
- } finally {
- parser.close();
- }
- }
第三个
- public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
- synchronized (mConstructorArgs) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
- final Context inflaterContext = mContext;
- final AttributeSet attrs = Xml.asAttributeSet(parser);
- Context lastContext = (Context) mConstructorArgs[0];
- mConstructorArgs[0] = inflaterContext;
- View result = root;
- try {
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG &&
- type != XmlPullParser.END_DOCUMENT) {
- }
- if (type != XmlPullParser.START_TAG) {
- throw new InflateException(parser.getPositionDescription()
- + ": No start tag found!");
- }
- final String name = parser.getName();
- //是否有merge
- if (TAG_MERGE.equals(name)) {
- if (root == null || !attachToRoot) {
- throw new InflateException("<merge /> can be used only with a valid "
- + "ViewGroup root and attachToRoot=true");
- }
- rInflate(parser, root, inflaterContext, attrs, false);
- } else {
- //一般都走这
- //创建临时View
- final View temp = createViewFromTag(root, name, inflaterContext, attrs);
- ViewGroup.LayoutParams params = null;
- //[重点]如果root不为null,才会走这
- if (root != null) {
- params = root.generateLayoutParams(attrs);
- //[重点]如果attachToRoot为false,表示不附加,仅仅设置LayoutParams
- if (!attachToRoot) {
- temp.setLayoutParams(params);
- }
- }
- rInflateChildren(parser, temp, attrs, true);
- //如果attachToRoot为true,把temp添加到root中。
- if (root != null && attachToRoot) {
- root.addView(temp, params);
- }
- //root为null时,返回temp
- //attachToRoot为false时,返回时临时创建的view,否则返回root
- if (root == null || !attachToRoot) {
- result = temp;
- }
- }
- } catch (XmlPullParserException e) {
- final InflateException ie = new InflateException(e.getMessage(), e);
- ie.setStackTrace(EMPTY_STACK_TRACE);
- throw ie;
- } catch (Exception e) {
- final InflateException ie = new InflateException(parser.getPositionDescription()
- + ": " + e.getMessage(), e);
- ie.setStackTrace(EMPTY_STACK_TRACE);
- throw ie;
- } finally {
- mConstructorArgs[0] = lastContext;
- mConstructorArgs[1] = null;
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- }
- return result;
- }
- }
重点关注root是否为null和attachToRoot是true还是false。
分下面3种情况
情况1
root为null时,返回的result是temp
情况2
root不为null,attachToRoot为true时,返回的是root
- View result = root;
-
- params = root.generateLayoutParams(attrs);
-
- //attachToRoot为true
- //附加,就添加到root中
- if (root != null && attachToRoot) {
- root.addView(temp, params);
- }
情况3
root不为null,attachToRoot为false时,返回的是temp
- params = root.generateLayoutParams(attrs);
-
- //attachToRoot = false
- //不附加,直接设置params
- if (!attachToRoot) {
- temp.setLayoutParams(params);
- }
-
- result = temp;
小结
Fragment和ListView或RecyclerVIew的适配器中,不需要附加到root中(root不为null,且attachToRoot为false)
推荐使用
- LayoutInflater.from(MediaApp.getContext()).inflate(R.layout.layout_item_media, parent, false)
当然也可以使用
- LayoutInflater.from(MediaApp.getContext()).inflate(R.layout.layout_item_media, null)
但容易出行《》这个问题,因为LayoutParams没有被应用到temp中。
Activity中setContentView()是需要附加到root中(root不为null,且attachToRoot为true)
- #PhoneWindow.java中
- public void setContentView(int layoutResID) {
- //略
- mLayoutInflater.inflate(layoutResID, mContentParent);
- //略
- }
参考文章
《》
《》
历史上的今天
- 《LayoutInflater之inflate()简单分析》
- 《25Qi导航》
- 《全国互联网安全管理平台》
- 《雷峰网》
- 《e站导航》
- 《网谋论坛》
- 《运营笔记导航》
- 《大佬论坛》
- 《禾坡网》
© 版权声明