目录
前言
简单记录一下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); //略 }
参考文章
-
《》
-
《》