Android LayoutInflater讲解

一、前言

以前用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 ,返回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,这才导致它的显示不正常,但为什么不正常,我还没找到原因。后面我会再找找。

五、结尾

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序编织梦想

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值