LayoutInflater attachToRoot参数是什么意思?

时间:2012-09-24 14:55:06

标签: android android-layout android-view layout-inflater

LayoutInflater.inflate文档对我来说并不十分清楚attachToRoot参数的用途。

  

attachToRoot :是否应将膨胀的层次结构附加到根参数?如果为false,则root仅用于创建正确的   LayoutParams的子类,用于XML中的根视图。

有人可以更详细地解释,特别是根视图是什么,并且可能会显示truefalse值之间行为变化的示例吗?

11 个答案:

答案 0 :(得分:88)

如果设置为true,那么当布局膨胀时,它将自动添加到第二个参数中指定的ViewGroup的视图层次结构中作为子级。例如,如果根参数为LinearLayout,那么您的虚增视图将自动添加为该视图的子视图。

如果设置为false,那么您的布局将会膨胀,但不会附加到任何其他布局(因此不会绘制,接收触摸事件等)。

答案 1 :(得分:68)

现在或不现在

"第三"之间的主要区别参数attachToRoot为true或false是这个。

  

当你把attachToRoot

true:将子视图添加到父现在正确
false:将子视图添加到父现在不是 稍后再添加。 `

  

之后何时

稍后您使用例如parent.addView(childView)

一个常见的误解是,如果attachToRoot参数为false,则子视图不会添加到父视图中。的 WRONG
在这两种情况下,子视图都将添加到parentView。这只是时间的问题。

inflater.inflate(child,parent,false);
parent.addView(child);   

相当于

inflater.inflate(child,parent,true);

A BIG NO-NO
如果您不负责将子视图添加到父级,则不应将attachToRoot传递为true 例如,添加片段

public View onCreateView(LayoutInflater inflater,ViewGroup parent,Bundle bundle)
  {
        super.onCreateView(inflater,parent,bundle);
        View view = inflater.inflate(R.layout.image_fragment,parent,false);
        .....
        return view;
  }

如果您将第三个参数传递为true,则会因此而获得IllegalStateException。

getSupportFragmentManager()
      .beginTransaction()
      .add(parent, childFragment)
      .commit();

由于您已经错误地在onCreateView()中添加了子片段。调用add会告诉您子视图已添加到父级,因此 IllegalStateException
这里你不负责添加childView,FragmentManager负责。所以在这种情况下总是传递假。

注意:我还读过,如果attachToRoot为false,则parentView将无法获取childView touchEvents。但我还没有测试过它。

答案 2 :(得分:35)

似乎响应中有很多文字,但没有代码,这就是为什么我决定用一个代码示例重新回答这个旧问题,在人们提到的几个回复中:

  

如果设置为true,那么当布局膨胀时,它将自动添加到第二个参数中指定的ViewGroup的视图层次结构中作为子项。

在代码中实际意味着什么(大多数程序员都理解):

public class MyCustomLayout extends LinearLayout {
    public MyCustomLayout(Context context) {
        super(context);
        // Inflate the view from the layout resource and pass it as child of mine (Notice I'm a LinearLayout class).

        LayoutInflater.from(context).inflate(R.layout.child_view, this, true);
    }
}

请注意,之前的代码正在添加布局R.layout.child_view作为MyCustomLayout的子级,因为attachToRoot param是true并且父级的布局参数完全相同好像我会以编程方式使用addView,或者就像我在xml中使用它一样:

<LinearLayout>
   <View.../>
   ...
</LinearLayout>

以下代码说明了将attachRoot作为false传递时的方案:

LinearLayout linearLayout = new LinearLayout(context);
linearLayout.setLayoutParams(new LayoutParams(
    LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
linearLayout.setOrientation(LinearLayout.VERTICAL);
    // Create a stand-alone view
View myView = LayoutInflater.from(context)
    .inflate(R.layout.ownRootView, null, false);
linearLayout.addView(myView);

在前面的代码中,您指定希望myView成为自己的根对象,并且不将其附加到任何父对象,稍后我们将其作为LinearLayout的一部分添加,但暂时这是一个独立的(没有父母)观点。

Fragments也会发生同样的事情,您可以将它们添加到已存在的组中并成为其中的一部分,或者只传递参数:

  

inflater.inflate(R.layout.fragment,null,false);

指定它将是它自己的根。

答案 3 :(得分:26)

文档和前两个答案应该足够了,只是我的一些想法。

inflate方法用于扩充布局文件。使用这些夸大的布局,您必须将它们直接附加到父ViewGroup,或者只是从该布局文件中扩展视图层次结构,并在普通视图层次结构之外使用它。

在第一种情况下,attachToRoot参数必须设置为true(或者更简单地使用带有布局文件和父根inflate的{​​{1}}方法}(非ViewGroup))。在这种情况下,返回的null只是在方法中传递的ViewViewGroup将添加膨胀的视图层次结构。

对于第二个选项,返回的ViewGroup是布局文件中的根View。如果您还记得我们include-merge pair question的最后一次讨论,这是ViewGroup限制的原因之一(当以merge为根的布局文件被夸大时,您必须提供父级并且merge必须设置为attachedToRoot)。如果您的根布局文件的true代码为mergeattachedToRoot设置为false,那么inflate方法将无法返回merge没有等价物。 此外,正如文档所述,将inflate设置为attachToRoot的{​​{1}}版本非常重要,因为您可以使用父级中的false创建视图层次结构。这在某些情况下很重要,最值得注意的是LayoutParams的子类,AdapterView的子类,不支持ViewGroup方法集。我确定你记得在addView()方法中使用这一行:

getView()

此行确保膨胀的convertView = inflater.inflate(R.layout.row_layout, parent, false); 文件在其根R.layout.row_layout上设置的LayoutParams子类中具有正确的AdapterView。如果您不这样做,如果根是ViewGroup,则布局文件可能会出现问题。 RelativeLayout也有一些特殊且重要的TableLayout/TableRow,您应确保其中的观看次数正确LayoutParams

答案 4 :(得分:17)

我自己也对attachToRoot方法中inflate的真正目的感到困惑。经过一些UI研究后,我终于得到了答案:

父级:

在这种情况下,

是使用findViewById()围绕要膨胀的视图对象的窗口小部件/布局。

attachToRoot:

将视图附加到其父级(包括它们在父级层次结构中),因此视图接收的任何触摸事件也将转移到父视图。现在它是由父母决定是否要接受这些事件或忽略它们。如果设置为false,它们不会被添加为父母的直接子女,父母也不会接受任何接触来自观点的事件。

希望这能解决困惑

答案 5 :(得分:9)

由于inflate()方法的文档,这个主题存在很多混淆。

通常,如果attachToRoot设置为true,则第一个参数中指定的布局文件会膨胀并附加到此时第二个参数中指定的ViewGroup。当attachToRoot为false时,第一个参数的布局文件会膨胀并作为View返回,任何View附件都会在其他时间发生。

除非你看到很多例子,否则这可能并不重要。在Fragment的onCreateView方法内部调用LayoutInflater.inflate()时,您需要为attachToRoot传入false,因为与该Fragment关联的Activity实际上负责添加Fragment的视图。如果您在稍后的某个时间点手动充气并将视图添加到另一个视图,例如使用addView()方法,则需要为attachToRoot传入false,因为附件会在稍后的时间点传递。

您可以在我撰写的有关此主题的博客文章中阅读有关对话框和自定义视图的其他几个独特示例。

https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/

答案 6 :(得分:7)

我写了这个答案,因为即使经过几个StackOverflow页面,我也无法清楚地了解attachToRoot的含义。下面是LayoutInflater类中的inflate()方法。

View inflate (int resource, ViewGroup root, boolean attachToRoot)

查看 activity_main.xml 文件, button.xml 布局以及我创建的 MainActivity.java 文件。

activity_main.xml中

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

</LinearLayout>

button.xml

<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

MainActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    LayoutInflater inflater = getLayoutInflater();
    LinearLayout root = (LinearLayout) findViewById(R.id.root);
    View view = inflater.inflate(R.layout.button, root, false);
}

当我们运行代码时,我们不会在布局中看到按钮。这是因为我们的按钮布局未添加到主活动布局中,因为attachToRoot设置为false。

LinearLayout有一个 addView(视图视图)方法,可用于将视图添加到LinearLayout。这会将按钮布局添加到主活动布局中,并在运行代码时使按钮可见。

root.addView(view);

让我们删除上一行,看看当我们将attachToRoot设置为true时会发生什么。

View view = inflater.inflate(R.layout.button, root, true);

我们再次看到按钮布局是可见的。这是因为attachToRoot直接将膨胀的布局附加到指定的父级。在这种情况下,根LinearLayout。在这里,我们不必像以前使用addView(View view)方法那样手动添加视图。

为什么人们在将片段的attachToRoot设置为true时会收到IllegalStateException。

这是因为对于片段,您已经指定了将片段布局放在活动文件中的位置。

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
    .add(R.id.root, fragment)
    .commit();

add(int parent,Fragment fragment)将具有它布局的片段添加到父布局中。如果我们将attachToRoot设置为true,您将获得IllegalStateException:指定的子节点已经有父节点。由于片段布局已经添加到add()方法中的父布局中。

当您对片段进行充气时,应始终为attachToRoot传递false。 FragmentManager的工作是添加,删除和替换碎片。

回到我的例子。如果我们两个都做了怎么办。

View view = inflater.inflate(R.layout.button, root, true);
root.addView(view);

在第一行中,LayoutInflater将按钮布局附加到根布局,并返回一个包含相同按钮布局的View对象。在第二行中,我们将相同的View对象添加到父根布局。这导致我们在Fragments中看到的IllegalStateException(指定的子节点已经有父节点)。

请记住,还有另一个重载的inflate()方法,默认情况下将attachToRoot设置为true。

View inflate (int resource, ViewGroup root)

答案 7 :(得分:0)

当您定义父级时,attachToRoot会确定您是否希望inflater实际将其附加到父级。在某些情况下,这会导致问题,例如在ListAdapter中,它会导致异常,因为列表会尝试将视图添加到列表中,但它表示已经附加了该视图。在其他情况下,你只需要自己膨胀视图以添加到一个活动,它可以很方便,并为您节省一行代码。

答案 8 :(得分:0)

例如,我们有一个ImageView,一个LinearLayout和一个RelativeLayout。 LinearLayout是RelativeLayout的子节点。 视图层次结构将是。

RelativeLayout
           ------->LinearLayout

我们有一个单独的ImageView布局文件

<强> image_view_layout.xml     

附加到root:

//here container is the LinearLayout

    View v = Inflater.Inflate(R.layout.image_view_layout,container,true);
  1. 这里v包含容器布局的参考,即 LinearLayout.and如果你想设置像ImageView setImageResource(R.drawable.np);这样的参数,你必须通过父母的引用找到它,即view.findById()
  2. v的父级将是FrameLayout。
  3. LayoutParams将为FrameLayout。
  4. 不附加到root:

    //here container is the LinearLayout
        View v = Inflater.Inflate(R.layout.image_view_layout,container,false);
    
    1. 此处v包含无参考容器布局但直接 引用膨胀的ImageView,以便设置它 像view.setImageResource(R.drawable.np);这样的参数没有 像findViewById一样参考。但是指定了容器 ImageView可以获取容器的LayoutParams 容器的引用仅适用于LayoutParams 别的。
    2. 所以在特殊情况下,Parent将为null。
    3. LayoutParams将属于LinearLayout。

答案 9 :(得分:0)

attachToRoot设置为true:

  

如果attachToRoot设置为true,则第一个参数中指定的布局文件会膨胀并附加到第二个参数中指定的ViewGroup。

想象一下,我们在XML布局文件中指定了一个按钮,其布局宽度和布局高度设置为match_parent。

<Button xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/custom_button">
</Button>

我们现在想要以编程方式将此Button添加到Fragment或Activity内的LinearLayout。如果我们的LinearLayout已经是一个成员变量mLinearLayout,我们只需添加以下按钮:

inflater.inflate(R.layout.custom_button, mLinearLayout, true);

我们指定要从布局资源文件中扩展Button;然后我们告诉LayoutInflater我们要将它附加到mLinearLayout。我们的布局参数很受尊重,因为我们知道Button被添加到LinearLayout。 Button的布局参数类型应为LinearLayout.LayoutParams。

attachToRoot设置为false(不要求使用false)

  

如果attachToRoot设置为false,则第一个参数中指定的布局文件会膨胀,并且会附加到第二个参数中指定的ViewGroup,但是膨胀的视图会获取父级&#39 ; s LayoutParams ,使该视图能够正确放入父级。


让我们来看看你想将attachToRoot设置为false的时间。在这种情况下,此时第二个参数中第一个参数inflate()中指定的视图未附加到ViewGroup。

回想一下我们之前的Button示例,我们要将布局文件中的自定义按钮附加到mLinearLayout。我们仍然可以通过为attachToRoot传入false来将我们的Button附加到mLinearLayout - 我们之后只需手动添加它。

Button button = (Button) inflater.inflate(R.layout.custom_button,    mLinearLayout, false);
mLinearLayout.addView(button);

当我们为attachToRoot传入true时,这两行代码等同于我们之前在一行代码中编写的内容。通过传入false,我们说我们不想将View附加到根ViewGroup。我们说它会在其他时间点发生。在这个例子中,另一个时间点就是在通货膨胀之下立即使用的addView()方法。

当我们手动将View添加到ViewGroup时,false attachToRoot示例需要更多工作。

attachToRoot设置为false(false为必填)

在onCreateView()中膨胀并返回Fragment的视图时,请务必为attachToRoot传入false。如果传入true,则会出现IllegalStateException,因为指定的子节点已经有父节点。您应该指定将Fragment的视图放回Activity中的位置。 FragmentManager的工作是添加,删除和替换碎片。

FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment =  fragmentManager.findFragmentById(R.id.root_viewGroup);

if (fragment == null) {
fragment = new MainFragment();
fragmentManager.beginTransaction()
    .add(R.id.root_viewGroup, fragment)
    .commit();
}

将在您的Activity中保存Fragment的root_viewGroup容器是Fragment中onCreateView()中为您提供的ViewGroup参数。它也是您传递给LayoutInflater.inflate()的ViewGroup。但是,FragmentManager将处理将Fragment的视图附加到此ViewGroup。你不想附加两次。将attachToRoot设置为false。

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

如果我们不想在onCreateView()中附加它,为什么我们首先给出Fragment的父ViewGroup?为什么inflate()方法请求根ViewGroup?

事实证明,即使我们没有立即将新增加的View添加到其父ViewGroup,我们仍然应该使用父的LayoutParams,以便新View在最终附加时确定其大小和位置。 / p>

链接:https://youtu.be/1Y0LlmTCOkM?t=409

答案 10 :(得分:0)

只分享我在从事本主题工作时遇到的一些观点,

除了接受的答案外,我还想指出一些可以有所帮助的观点。

因此,当我将 attachToRoot 用作true时,返回的视图的类型为 ViewGroup 父级的根ViewGroup,它是作为 inflate(layoutResource,ViewGroup,attachToRoot) 方法的参数传递的,不是通过布局的类型而是在 attachToRoot 为假,我们得到该layoutResource的根 ViewGroup 的函数返回类型。

让我举例说明:

如果我们有 LinearLayout 作为 root 布局,然后我们要添加 < em> TextView 通过 inflate 功能。

然后使用 attachToRoot 作为 true 的膨胀函数返回 视图 LinearLayout

的em>

在将 attachToRoot 用作 false 时,膨胀函数返回 视图 TextView

的em>

希望这个发现会有所帮助...