LayoutInflater

LayoutInflater主要用来加载布局。

在Activity中,通常都是调用 setContentView() 来加载布局,其实该方法的内部也是使用LayoutInflater 来加载布局的,只不过这部分源码是internal的,不太容易查看到。

如何创建LayoutInflater实例?

三种方式:

// 方式1
LayoutInflater layoutInflater = LayoutInflater.from(context); 
// 方式2
LayoutInflater layoutInflater = (LayoutInflater) context
        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// 方式3
LayoutInflater inflater = getLayoutInflater(); //调用Activity的getLayoutInflater() 

方式1其实是对方式2做的封装,内部调用了方式2来返回一个LayoutInflater;而方式3内部调用的其实就是方式2,即:

    getLayoutInflater() → LayoutInflater.from(context) → context.getSystemService(xx)


得到了LayoutInflater的实例之后就可以调用它的inflate()方法来加载布局了,如下所示:

//方法1:root表示给该布局的外部再嵌套一层父布局,一般不需要,直接传null
View layout1 = layoutInflater.inflate(R.layout.xx, root);

//方法2:root同样表示给该布局嵌套父布局;
//attachToRoot为true,表示嵌套到root中,也就和方法1一样;所以需要传false才会用到方法2
//attachToRoot为false,表示不嵌套到root中,可能需要我们手动添加,即,root.addView(layout2);
//想让布局的LayoutParams起作用但又不想重复嵌套到root中,可以使用此方法,典型例子是Fragment的onCreateView()
View layout2 = layoutInflater.inflate(int resource, ViewGroup root, boolean attachToRoot) 

通常,我们还会采取下面方式加载布局:

View layout = View.inflate(context, R.layout.xx, root);
View.inflate() 其实就是通过上面的 LayoutInflater.inflate(context) 创建 LayoutInflater实例,然后还是调用它的 inflate() 来加载布局。


接下来我们就从源码的角度上看一看LayoutInflater到底是如何工作的。

不管你是使用的哪个inflate()方法的重载,最终都会辗转调用到LayoutInflater的如下代码中

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
    synchronized (mConstructorArgs) {
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        mConstructorArgs[0] = mContext;
        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();
            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, attrs);//循环遍历这个根布局下的子元素
            } else {
                View temp = createViewFromTag(name, attrs);//根据节点名来创建View对象
                ViewGroup.LayoutParams params = null;
                if (root != null) {
                    params = root.generateLayoutParams(attrs);
                    if (!attachToRoot) {
                        temp.setLayoutParams(params);
                    }
                }
                rInflate(parser, temp, attrs);
                if (root != null && attachToRoot) {
                    root.addView(temp, params);
                }
                if (root == null || !attachToRoot) {
                    result = temp;
                }
            }
        }
        ...
        return result;
    }
}
    从这里我们就可以清楚地看出,LayoutInflater其实就是使用Android提供的pull解析方式来解析布局文件的。

    第23行,调用了createViewFromTag()这个方法,并把节点名和参数传了进去。看到这个方法名,我们就应该能猜到,它是用于根据节点名来创建View对象的。确实如此,在createViewFromTag()方法的内部又会去调用createView()方法,然后使用反射的方式创建出View的实例并返回。当然,这里只是创建出了一个根布局的实例而已,接下来会在第31行调用rInflate()方法来循环遍历这个根布局下的子元素,遍历时也是调用createViewFromTag()方法来创建View的实例,然后还会递归调用rInflate()方法来查找这个View下的子元素,每次递归完成后则将这个View添加到父布局当中。这样的话,把整个布局文件都解析完成后就形成了一个完整的DOM结构,最终会把最顶层的根布局返回


  平时我们经常使用layout_width和layout_height来设置View的大小,并且一直都能正常工作,就好像这两个属性确实是用于设置View的大小的。而实际上则不然,它们其实是用于设置View在布局中的大小的,也就是说,首先View必须存在于一个布局中,之后如果将layout_width设置成match_parent表示让View的宽度填充满布局,如果设置成wrap_content表示让View的宽度刚好可以包含其内容,如果设置成具体的数值则View的宽度会变成相应的数值。这也是为什么这两个属性叫作layout_width和layout_height,而不是width和height。


参考

1、Android LayoutInflater原理分析,带你一步步深入了解View(一) 

2、关于inflate的第3个参数

3、LayoutInflater——80%的Android程序员对它并不了解甚至错误使用

4、Layout Inflation不能这么用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值