android LayoutInflater

198 阅读3分钟

android LayoutInflater

​ android开发了几年,对于布局填充器(LayoutInfalter)这个类的功能并不会陌生,但是从来没有看过起源码,常常会因为parent,attachToRoot这几个参数的传法而出问题,今天正好碰上又传错了参数,所以抽时间捋一下源码,和各个参数的使用方法。

​ android中将一个xml转换成view我们一般会使用 View.inflateLayoutInflater.inflate这两个方法,但是这两个方法究竟有什么区别, 基于android-29 源码进行分析,不想看源码的可以直接查看结论。

一、View.inflate

public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
    LayoutInflater factory = LayoutInflater.from(context);
    return factory.inflate(resource, root);
}

​ 可以发现View.inflate最终调用的是LayoutInflater.inflate方法,所以最终弄懂LayoutInflater.inflate即可

public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
    
    return inflate(parser, root, root != null);
}

//最终调用的是LayoutInflater中的一个infalte重载方法

//当root 传入有值的时候,attachToRoot就是true

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)

​ 所以最核心的地方归结到inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)这个方法中来。

View.inflate方法其实就是对LayoutInfalter中的方法的包装,方便开发者使用。

二、LayoutInflater.inflate

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        ... 忽略 后续版本添加的提升性能方法 ...
        
        //Resource 获取到Xml的解析器  
        XmlResourceParser parser = res.getLayout(resource);
        try {
            // 调用LayoutInflater的infate重载方法
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    // 同步方法
    synchronized (mConstructorArgs) {
        final Context inflaterContext = mContext;
        //解析出xml中各个属性集合
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        Context lastContext = (Context) mConstructorArgs[0];
        mConstructorArgs[0] = inflaterContext;
        
        //默认返回的就是传入的root 如果出现任何的Exception
        View result = root;

        try {
            //校验是否是闭合的节点
            advanceToRootNode(parser);
            // 获取根节点的名字
            final String name = parser.getName();

            ...省略中间调试代码...
          
            if (TAG_MERGE.equals(name)) {
                ...省略对merge标签的处理...
            } else {
                //对普通view标签的处理
                // Temp is the root view that was found in the xml
                //生成传入xml文件的根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);
                    //如果不添加到传入的parent布局   则直接设置这个layoutparams 这个时候其实就是							warp_content  因为没有父布局的信息
                    if (!attachToRoot) {
                        temp.setLayoutParams(params);
                    }
                }

                //解析xml跟布局中的子布局
                rInflateChildren(parser, temp, attrs, true);

                //如果传入的parent不为空  切xml需要贴到parent的view中
                //这个时候子view 没有布局参数  参数都是warp
                if (root != null && attachToRoot) {
                    root.addView(temp, params);
                }
				
                //如果传入的root为空  或者不用贴到布局之上  直接返回xml布局view
                if (root == null || !attachToRoot) {
                    result = temp;
                }
            }

        } 
		...异常处理...
        return result;
    }
}

最终都会调用到public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) 这个方法中来,返回最终xml对用的view,但是传入的参数不同,造成的结果也会存在差异

三、结论

  • 使用LayoutInflater这个类比使用View的静态方法更灵活,推荐直接使用LayoutInflater

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)

  • 对于在adapter中使用的时候 传 root attachToRoot = false
  • attachToRoot = true root != null 可以获取到root的布局参数

作为个人笔记使用,有问题一起探讨!勿喷