LayoutInflater.inflate()之attachToRoot
Posted 码农000
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LayoutInflater.inflate()之attachToRoot相关的知识,希望对你有一定的参考价值。
将XML布局文件转化为View或者Widget时,经常使用LayoutInfater.infate()&&View.inflate()
View.inflate()源代码如下:
/**
* Inflate a view from an XML resource. This convenience method wraps the @link
* LayoutInflater class, which provides a full range of options for view inflation.
*
* @param context The Context object for your activity or application.
* @param resource The resource ID to inflate
* @param root A view group that will be the parent. Used to properly inflate the
* layout_* parameters.
* @see LayoutInflater
*/
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root)
LayoutInflater factory = LayoutInflater.from(context);
return factory.inflate(resource, root);
可见View.inflate()是通过LayoutInfater.inflate()实现了xml文件到View或者Widget的转化的。下面讲述一下LayoutInfater.inflate()中关于attachToRoot参数的注意事项。
/**
* Inflate a new view hierarchy from the specified xml resource. Throws
* @link InflateException if there is an error.
*
* @param resource ID for an XML layout resource to load (e.g.,
* <code>R.layout.main_page</code>)
* @param root Optional view to be the parent of the generated hierarchy (if
* <em>attachToRoot</em> is true), or else simply an object that
* provides a set of LayoutParams values for root of the returned
* hierarchy (if <em>attachToRoot</em> is false.)
* @param attachToRoot Whether the inflated hierarchy should be attached to
* the root parameter? If false, root is only used to create the
* correct subclass of LayoutParams for the root view in the XML.
* @return The root View of the inflated hierarchy. If root was supplied and
* attachToRoot is true, this is root; otherwise it is the root of
* the inflated XML file.
*/
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();
这一段注释有点长,再摘抄一下,大家请看下面的内容:
* @param attachToRoot Whether the inflated hierarchy should be attached to
* the root parameter? If false, root is only used to create the
* correct subclass of LayoutParams for the root view in the XML.
如果attachToRoot=true,则布局文件将转化为View并绑定到root,然后返回root作为根节点的整个View;如果attachToRoot=false,则布局文件转化为View但不绑定到root,返回以布局文件根节点为根节点的View。在这两种情况下,root的LayoutParams***都*会影响布局文件的显示样式。下面分情况对attachToRoot的参数的设置进行讲解。
attachToRoot=true
- 将一个xml布局文件放入组件
item_linearout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical" >
</LinearLayout>
非常简单的布局,注意它的背景颜色是白色!
item_btn.xml
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff0000" >
</Button>
注意他的背景是红色
MainActivity主要代码:
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.item_linearout);
final LinearLayout outLayout = (LinearLayout) findViewById(R.id.ll);
LayoutInflater inflater = LayoutInflater.from(this);
inflater.inflate(R.layout.item_btn, outLayout, true);
运行效果
ok,将他放入了linearlaout中。
- 自定义组件
view_with_merge_tag.xml文件
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0f0"
android:text="Hello World!" >
</TextView>
</merge>
为了减少布局参差的嵌套,这是用了merge.注意其背景会绿色。
CustomLinear.java
package com.example.androidtest;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
public class CustomLinear extends LinearLayout
public CustomLinear(Context context, AttributeSet attrs)
super(context, attrs);
init();
public CustomLinear(Context context)
super(context);
init();
private void init()
LayoutInflater inflater = LayoutInflater.from(getContext());
inflater.inflate(R.layout.view_with_merge_tag, this);
MainActivity.java主要代码
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(new CustomLinear(this));
运行效果:
ok!
attachToRoot =false
attacheToRoot就是暂时不将布局文件放入root中。现在修改第一个例子,将attachToRoot设置为false,实现同样的效果。
MainActivity的主要代码:
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.item_linearout);
final LinearLayout outLayout = (LinearLayout) findViewById(R.id.ll);
LayoutInflater inflater = LayoutInflater.from(this);
Button button = (Button) inflater.inflate(R.layout.item_btn, outLayout,
false);
outLayout.addView(button);
这里将attachToRoot设置为false,没有将button立即加入linearlayout中,稍后的addView,将button添加到了linearlayout中。
布局文件转换为View,不都是为了放在组件,显示在界面上吗?为何还要多此一举,分两步来做这样的事情呢?Android中有些组件有其自身的”add View”机制,如:Fragment.onCreateView
package com.example.androidtest;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class CustomFragment extends Fragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
View view = inflater.inflate(R.layout.item_btn, container, false);
return view;
在Activty中,
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.item_linearout);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
fragmentTransaction.replace(R.id.ll, new CustomFragment());
fragmentTransaction.commit();
“add View”的逻辑由FragmentTransaction 进行处理了,如果我们这里将attachToRoot设置为true,就会有IllegalStateException.
所以组件自己有“add View”的机制,我们就不要再画蛇添足了。
可能会有疑问既然没有将创建的View 添加到root中,为什么还要添加root参数呢?直接使用
View view = inflater.inflate(R.layout.item_btn,null);
不是更简单吗?如果这样写,lint会给出警告
Avoid passing null as the view root (needed to resolve layout parameters on the inflated layout’s root element)。
xml布局文件在解析成View的时候,需要root的布局信息(View的布局参数要受到root的限制)。如果不填写root,使用xml布局文件生成View的时候就会使用默认的LayoutParams.而这个不一定是符合要求的,View可能比设定的要小。
不设置root
有些时候无法明确的知道View的root,eg.当你实例化一个AlterDialog.
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.item_linearout);
LayoutInflater inflater = LayoutInflater.from(this);
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
View customView = inflater.inflate(R.layout.item_btn, null);
dialogBuilder.setView(customView);
dialogBuilder.show();
在这种情况下,传递null 就可以了。通用的规则是,如果可以知道root,一定要传递这个参数。
结论
- 如果知道root,一定要传,尽量避免传null
- 当不需要将布局文件生成的View添加到组件中时(组件有其自身的添加逻辑),attachToRoot设置成false.
- 当View已经添加到一个root中时,attachToRoot设置成false.
- 自定义组件应该将attachToRoot设置成true.
翻译地址
https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/
以上是关于LayoutInflater.inflate()之attachToRoot的主要内容,如果未能解决你的问题,请参考以下文章
LayoutInflater inflate LayoutParams 简介
LayoutInflater.inflate()方法的ViewGroup参数问题
LayoutInflater.inflate()方法的ViewGroup参数问题