以前用adapter的时候每次就知道copy代码,对LayoutInflater不求深入了解,真是惭愧!今天抽空把LayoutInflater的源码看了看,终于有些感悟,记录下来,希望对大家有些帮助。
我们最常用的就是以下2个方法:
1.LayoutInflater.from(MainActivity.this).inflate(int resource, ViewGroup root); 2.LayoutInflater.from(MainActivity.this).inflate(int resource, ViewGroup root, boolean attachToRoot);第1个方法的源码是这样的:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) { return inflate(resource, root, root != null); }可以看的出来,在方法中调用的是第2中方法,这样算起来就剩下第2个方法了,使有的时候会出现这二种情况:
1、convertView = mInflater.inflate(R.layout.item, parent ,false); 2、convertView = mInflater.inflate(R.layout.item, parent ,true);可以看的出,它们的区别就在于最后一个boolean类型的参数,那它们二个有什么区别呢?我们先来看个例子
我们在Linearnlayout中分别添加以下三种情况的view,看看它们的显示:
inflate(layoutId, null ) inflate(layoutId, root, false ) inflate(layoutId, root, true )1.inflate(layoutId, null )的情况 2.inflate(layoutId, root, false ) 的情况 3.inflate(layoutId, root, true ) 的情况 是的,要相信自己,你没看错,第3种情况确实是报错了! 由上面的效果图我想信大家一定有这几个疑问:
1.为什么情况1宽度是整个屏幕,情况2是自适应。 2.为什么情况3报错了。接下来,我们来看看源码,找出是什么导致了这些情况!
上面我们分析过,这3中情况最后都调用了一个方法,我们来看看它的源码:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { .... //先将root赋值给result,后面我们会根据不同的参数改变result值,最后我们要返回resulut; View result = root; try { // Look for the root node. ... 这块是用解析xml布局代码 ... if (TAG_MERGE.equals(name)) { ... } else { // Temp is the root view that was found in the xml //在createViewFromTag()方法的内部调用createView()方法,然后使用反射的方式创建出View的实例并返回。 final View temp = createViewFromTag(root, name, inflaterContext, attrs); 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) // 当root不为null,attachToRoot为false时,为temp设置了LayoutParams. temp.setLayoutParams(params); } } // Inflate all children under temp against its context. rInflateChildren(parser, temp, attrs, true); // We are supposed to attach all the views we found (int temp) // to root. Do that now. //重点看这里,如果root不为空 并且 attachToRoot为true的时候,给temp添加LayoutParams属性 if (root != null && attachToRoot) { root.addView(temp, params); } // Decide whether to return the root that was passed in or the // top view found in xml. //重点看这里,如果root不为空 并且 attachToRoot为 false的时候,直接返回temp 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 { // Don't retain static reference on context. mConstructorArgs[0] = lastContext; mConstructorArgs[1] = null; Trace.traceEnd(Trace.TRACE_TAG_VIEW); } return result; } }这了让大家看的简单一点,我省略了很多无用的代码,大家看源码中有汉字的地方,那是重点。 1:声明了View result = root ;//最终返回值为result 2:temp = createViewFromTag(root, name, attrs);创建了View 3:
if(root!=null) { params = root.generateLayoutParams(attrs); if (!attachToRoot) { temp.setLayoutParams(params); } }当root不为null,attachToRoot为false时,为temp设置了LayoutParams. 4:
if (root != null && attachToRoot) { root.addView(temp, params); }当root不为null,attachToRoot为true时,将tmp按照params添加到root中。 5.
if (root == null || !attachToRoot) { result = temp; }如果root为null,或者attachToRoot为false则,将temp赋值给result。 最后返回result。 经上面的分析,我们可以得到以下结论:
从上面的分析已经可以看出: 1.Inflate(resId , null ) 只创建temp ,返回temp 2.Inflate(resId , parent, false )创建temp,然后执行temp.setLayoutParams(params);返回temp 3.Inflate(resId , parent, true ) 创建temp,然后执行root.addView(temp, params);最后返回root由此我们可以解释为什么情况3会报错:
因为root.addView(temp, params)这句话执行了,我们的contentLayout已经将button添加到里面去了,如果我们再调用contentLayout.addView(view3);它肯定会报错的。我们可以验证一下,代码如下:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); contentLayout = (LinearLayout) findViewById(R.id.contentLayout); View view3 = LayoutInflater.from(MainActivity.this).inflate(R.layout.item,contentLayout,true); }这们在这里只是调用了inflate(R.layout.item,contentLayout,true);并没有调用contentLayout.addView(view3)。看一下效果图:
看到没有,这就证实了我们上面的分析:
Inflate(resId , parent, true ) 创建temp,然后执行root.addView(temp, params);最后返回root再在看看第2个坑:为什么情况1的宽度是整个屏幕,情况2是自适应呢?并且在情况1中不管button设置成多大的值,它的宽度都是整个屏幕。 这里我要向大家道歉,因为我也不知道为什么。对不起。在上面我们分析了:
Inflate(resId , null ) 只创建temp ,返回temp只创建view,并没有设置LayoutParams,这才导致它的显示不正常,但为什么不正常,我还没找到原因。后面我会再找找。
好了就讲到这里吧,希望对大家有所帮助。 在技术上我依旧是个小渣渣,加油!勉励自己!