Android知识整理Android Developer首页投稿(暂停使用,暂停投稿)

Android下LayoutInflater的正确使用姿势

2016-11-17  本文已影响2166人  DevWang
生成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加载解析机制源码分析

上一篇下一篇

猜你喜欢

热点阅读