Android下LayoutInflater的正确使用姿势

作者: DevWang | 来源:发表于2016-11-17 18:05 被阅读2166次
    生成LayoutInflater实例:
    // 方法1
    LayoutInflater mInflater = LayoutInflater.from(context);
    // 方法2
    LayoutInflater mInflater = (LayoutInflater) context  
        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    
    最常用加载布局的方法:
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        ...省略实现代码...
    }
    
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {    
        ...省略实现代码...
    }
    
    直接放上结论并验证讲解:
    阅读源代码可知:
    1. inflate(resource, null);
      只创建view,view没有LayoutParams值,然后直接返回view.
      xml布局中最外层的layout_width、layout_height将失效.
    2. inflate(resource, null, true);
      同1.
    3. inflate(resource, null, false);
      同1.
    4. inflate(resource, root);
      创建view,然后执行root.addView(view, params),最后返回root.
      params为父布局根据xml布局中的宽和高得到相应的LayoutParams
    5. inflate(resource, root, true);
      同4.
    6. inflate(resource, root, false);
      创建view,然后执行view.setLayoutParams(params),然后返回view.
      params为父布局根据xml布局中的宽和高得到相应的LayoutParams
    总结如下:
    1. 如果root为null,attachToRoot将失去作用,设置任何值都没有意义,加载的布局文件最外层的所有layout属性会失效,由父布局来重新指定.
    2. 如果root不为null,attachToRoot不论是true或false,加载的布局文件最外层的所有layout属性都有效,唯一的不同是:
      attachToRoot为true时,会自动调用root.addView(view, params),最后返回root;
      attachToRoot为false时,会返回view,需手动调用root.addView(view, params).
    3. 在不设置attachToRoot参数的情况下,如果root不为null,attachToRoot参数默认为true.

    废话不多说,直接上代码,首先定义三个xml:

    main.xml - 此布局为父布局
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    
        android:id="@+id/content"    
        android:layout_width="match_parent"    
        android:layout_height="match_parent"
        android:background="#519C9A"
        android:orientation="vertical" />
    
    item1.xml - 此布局高度固定
    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"    
        android:layout_width="100dp"    
        android:layout_height="50dp"    
        android:background="#C5C1AA"    
        android:gravity="center"    
        android:text="我是内容"    
        android:textStyle="bold" />
    
    item2.xml - 此布局高度自适应
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    
        android:layout_width="wrap_content"    
        android:layout_height="wrap_content"
        android:background="#A4A4A4">    
        <TextView
            android:layout_width="100dp"        
            android:layout_height="50dp"        
            android:background="#C9C9C9"        
            android:gravity="center"        
            android:text="我是内容"        
            android:textStyle="bold" />
    </RelativeLayout>
    
    1. 如果root为null,attachToRoot将失去作用,设置任何值都没有意义,加载的布局文件最外层的所有layout属性会失效,由父布局来重新指定.
    content.addView(mInflater.inflate(R.layout.item1, null));
    content.addView(mInflater.inflate(R.layout.item1, null, true));
    content.addView(mInflater.inflate(R.layout.item1, null, false));
    
    content.addView(mInflater.inflate(R.layout.item2, null));
    content.addView(mInflater.inflate(R.layout.item2, null, true));
    content.addView(mInflater.inflate(R.layout.item2, null, false));
    

    小伙伴们看到这里是不是感觉晕晕的,明明item1.xml和item2.xml中根布局设置的宽度不是match_parent,结果却是充满屏幕宽?
    在上面我们指出:这种方法的调用只是根据xml布局创建了view,view没有设置LayoutParams值,而是在父布局调用addView(...)方法时给定params,下面给出代码:

    addView(...)方法是在ViewGroup.java中:

    public void addView(View child, int index) {    
        if (child == null) {        
            throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");    
        }    
        LayoutParams params = child.getLayoutParams();    
        if (params == null) {        
            params = generateDefaultLayoutParams();        
            if (params == null) {            
                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");        
            }    
        }    
        addView(child, index, params);
    }
    

    因为我们这里父布局root是LinearLayout,所以params变量是由调用了LinearLayout.java中的generateDefaultLayoutParams()方法生成

    protected LayoutParams generateDefaultLayoutParams() {
        // 水平布局下 宽度高度自适应    
        if (mOrientation == HORIZONTAL) {        
            return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        // 竖直布局下 宽充满屏幕,高度自适应
        } else if (mOrientation == VERTICAL) {        
            return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);    
        }    
        return null;
    }
    

    看到这里有没有一种恍然大悟的感觉!

    2. 如果root不为null,attachToRoot不论是true或false,加载的布局文件最外层的所有layout属性都有效,唯一的不同是:

    attachToRoot为true时,会自动执行root.addView(view, params),最后返回root;
    attachToRoot为false时,会返回view,需手动调用执行root.addView(view, params).

    // 情景1 - attachToRoot为true
    mInflater.inflate(R.layout.item1, content, true);
    mInflater.inflate(R.layout.item2, content, true);
    
    // 情景2 - attachToRoot为false
    content.addView(mInflater.inflate(R.layout.item1, content, false));
    content.addView(mInflater.inflate(R.layout.item2, content, false));
    

    情景1和情景2任选一个运行得到的结果如下:

    3. 在不设置attachToRoot参数的情况下,如果root不为null,attachToRoot参数默认为true.

    直接上源码,一目了然:

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

    至此,文章结束,希望此文能帮助到你,如果对此文有不同见解,欢迎直接评论!

    参考文档:
    关于LayoutInflater的错误用法
    Android LayoutInflater原理分析,带你一步步深入了解View(一)
    Android应用setContentView与LayoutInflater加载解析机制源码分析

    相关文章

      网友评论

      本文标题:Android下LayoutInflater的正确使用姿势

      本文链接:https://www.haomeiwen.com/subject/fjqrpttx.html