Android LayoutInflater讲解

来源:互联网 发布:gal一般用哪种编程语言 编辑:程序博客网 时间:2024/06/07 22:52

一、前言

以前用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 ) 

Activity代码

private LinearLayout contentLayout;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        contentLayout = (LinearLayout) findViewById(R.id.contentLayout);//      View view1 = LayoutInflater.from(MainActivity.this).inflate(R.layout.item,null);//      View view2 = LayoutInflater.from(MainActivity.this).inflate(R.layout.item,contentLayout,false);//      View view3 = LayoutInflater.from(MainActivity.this).inflate(R.layout.item,contentLayout,true);//      contentLayout.addView(view1);//情况1//      contentLayout.addView(view2);//情况2//      contentLayout.addView(view3);//情况3    }

activity_main代码:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/contentLayout"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"></LinearLayout>

item代码:

<?xml version="1.0" encoding="utf-8"?><Button xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="button"    />

显示如下:

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 ,返回temp2.Inflate(resId , parent, false )创建temp,然后执行temp.setLayoutParams(params);返回temp3.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,这才导致它的显示不正常,但为什么不正常,我还没找到原因。后面我会再找找。

五、结尾

好了就讲到这里吧,希望对大家有所帮助。
在技术上我依旧是个小渣渣,加油!勉励自己!

1 0
原创粉丝点击