View.inflate() 和LayoutInflater.inflate()的区别

前言:

既然在平淡的一天,也要坚持给自己找点乐趣。

 在android开发中,根据布局文件生成View的情况咋们开发中是十分常见的,现在咋们分析下这这两个方法的区别

LayoutInflater.inflate() ?  

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
    final Resources res = getContext().getResources();
    if (DEBUG) {
        Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                + Integer.toHexString(resource) + ")");
    }

    final XmlResourceParser parser = res.getLayout(resource);
    try {
        return inflate(parser, root, attachToRoot);       ----------------发现调用了这个重载的方法
    } finally {
        parser.close();
    }
}

三个参数分别为  1 ->你要生成的View 的XML文件,2  -> 父类布局   3 ->是否将xml生成的View添加到 父布局中

下面先讲解上面的方法

final Resources res = getContext().getResources();          -------------获取Resources 对象

final XmlResourceParser parser = res.getLayout(resource);  ------------获取XML的解析器(在LayoutInflater.inflate() 方法中也可以直接传入解析器,但是一般咋们不这样做,麻烦不好看

接下来我们进入它调用的重载的方法中去

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    synchronized (mConstructorArgs) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

        final Context inflaterContext = mContext;
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        Context lastContext = (Context) mConstructorArgs[0];
        mConstructorArgs[0] = inflaterContext;

        //默认结果为传入的父布局
        View result = root;

        try {
            // Look for the root node.
            int type;
            while ((type = parser.next()) != XmlPullParser.START_TAG &&
                    type != XmlPullParser.END_DOCUMENT) {
                // Empty
            }

            if (type != XmlPullParser.START_TAG) {
                throw new InflateException(parser.getPositionDescription()
                        + ": No start tag found!");
            }

            final String name = parser.getName();

            if (DEBUG) {
                System.out.println("**************************");
                System.out.println("Creating root view: "
                        + name);
                System.out.println("**************************");
            }

            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, inflaterContext, attrs, false);
            } else {
                // Temp is the root view that was found in the xml
                //这个temp 就是根据你传入的XML文件生成的View
                final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                ViewGroup.LayoutParams params = null;
                
                 //如果父布局不为空
                if (root != null) {
                    if (DEBUG) {
                        System.out.println("Creating params from root: " +
                                root);
                    }
                    // Create layout params that match root, if supplied
                    // 为传入的布局生成一套匹配root的 LayoutParams  (源码的翻译)
                    params = root.generateLayoutParams(attrs);   
                    if (!attachToRoot) {
                        // Set the layout params for temp if we are not
                        // attaching. (If we are, we use addView, below)
                         // 如果没有 attachToRoot,那为根布局设置 layoutparams
                        temp.setLayoutParams(params);
                    }
                }

                if (DEBUG) {
                    System.out.println("-----> start inflating children");
                }

                // Inflate all children under temp against its context.
                rInflateChildren(parser, temp, attrs, true);

                if (DEBUG) {
                    System.out.println("-----> done inflating children");
                }

                // We are supposed to attach all the views we found (int temp)
                // to root. Do that now.
                //如果root 不等于null并且attachToRoot == true的时候,给你传入的View添加到root里面,返回给你
                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 == null 或者 attachToRoot = false 的时候 直接把XML生成的View返回
                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;
    }
}

现在看看两个参数的方法

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
    return inflate(resource, root, root != null); 
}

在上面的两个参数的最后发现还是调用inflate()三个参数的,只是默认给attachToRoot 传入值 为 root != null

如果 root == null,那么attachToRoot = false 返回你传入的View,并且不设置params

如果 root !=null  , 那么attachToRoot  = true  返回rootview ,设置了params

View.inflate();

最后看下View的inflate()

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

发现最后还是LayoutInflater.inflate(resource, root)两个参数的方法

                                            总结:


  • View.inflate() 只是一个android 提供的简易的包装方法 ,实际还是调用了LayoutInflater.inflate(resource, root)
  • LayoutInflater#inflate 由于可以自己选择 root 和 attachToRoot 的搭配(后面有解释),使用起来更加灵活;

  • 当  root 传空时,会直接返回要加载的 layoutId,返回的 View 没有父布局且没有 LayoutParams;

     问题?

假如我需要生成View,而且需要添加params,怎么办?

1.列如

RecycleView # onCreatViewHolder

在为 RecyclerView 创建 ViewHolder 时,由于 View 复用的问题,是 RecyclerView 来决定什么时候展示它的子View,这个完全不由我们决定,这种情况下,attachToRoot 必须为 false:

View.inflate() 这个方法肯定是不能的,这个这个方法的局限性

所以只能用LayoutInflater#inflate

public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
      LayoutInflater inflater = LayoutInflater.from(getActivity());  
      View view = inflater.inflate(R.layout.item, parent, false);  
      return new ViewHolder(view);  
}

2.列如

  • Fragment#onCreateView()

由于 Fragment 需要依赖于 Activity 展示,一般在 Activity 中也会有容器布局来盛放 Fragment:

Fragment fragment = new Fragment();
getSupportFragmentManager()
        .beginTransaction()
        .add(R.id.root_container, fragment)
        .commit(); 

上述代码中的 R.id.root_container 便为容器,这个 View 会作为参数传递给 Fragment#onCreateView() :

public View onCreateView(LayoutInflater inflater, ViewGroup container, 
                         Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment_layout, parentViewGroup, false); 
}

它也是你在 inflate() 方法中传入的 ViewGroup,FragmentManager 会将 Fragment 的 View 添加到 ViewGroup 中,言外之意就是,Fragment 对应的布局展示或者说添加进 ViewGroup 时也不是我们来控制的,而是 FragmentManager 来控制的。

如果return inflater.inflate(R.layout.fragment_layout, parentViewGroup, true );

则会报一个错误

child already has parent 

在这里在引申一个问题,在代码生成的Fragment的生命周期,在没有添加到Activity 的时候只会走空参构造方法,其他的方法只有add()后才会走

这里有一个很好的解释因为

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
   Log.e("++", "onCreateView");
   return super.onCreateView(inflater, container, savedInstanceState);
}

中的container是在FragmentMange中的add()方法传入的

猜你喜欢

转载自blog.csdn.net/xueyoubangbang/article/details/89681598
今日推荐