251

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

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

有人可以更详细地解释一下,特别是根视图是什么,并可能展示一个值之间的行为变化的例子truefalse

4

12 回答 12

234

现在或现在

“第三个”参数 attachToRoot 是 true 还是 false 的主要区别是这个。

当你把 attachToRoot

true :立即将子视图添加到父视图false:将子视图添加到父视图NOT NOW。 稍后添加。`

以后是什么时候?

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

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

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

相当于

inflater.inflate(child,parent,true);

一个大
禁忌当您不负责将子视图添加到父视图时,您永远不应该将 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负责。所以在这种情况下总是传递 false 。

注意:我还读到,如果 attachToRoot 为 false,则 parentView 将不会获得 childView touchEvents。但是我还没有测试过。

于 2017-08-22T05:49:45.267 回答
99

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

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

于 2012-09-24T14:58:55.200 回答
41

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

如果设置为 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因为attachToRootparam istrue并以与我将以编程方式使用的方式完全相同的方式分配父级的布局参数addView,或者就像我在 xml 中执行此操作一样:

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

以下代码解释了传递attachRootas时的场景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但有一段时间它是一个独立(无父)视图。

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

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

指定它将是它自己的根。

于 2014-01-07T19:12:19.263 回答
27

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

inflate方法用于扩充布局文件。对于那些膨胀的布局,您必须能够将它们直接附加到父ViewGroup级,或者只是从该布局文件膨胀视图层次结构并在正常视图层次结构之外使用它。

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

对于第二个选项,返回View的是布局文件的根ViewGroup。如果您还记得我们在include-merge配对问题merge中的最后讨论,这是' 限制的原因之一(当merge以 root 身份扩展布局文件时,您必须提供父级并且attachedToRoot必须设置为true)。如果您有一个带有根 amerge标记的布局文件并attachedToRoot设置为,false那么该inflate方法将没有任何返回,因为merge没有等效项。此外,正如文档所述,设置为的inflate版本很重要,因为您可以使用正确的视图层次结构创建attachToRootfalseLayoutParams从父母。这在某些情况下很重要,最值得注意的是AdapterView的子类 的子类ViewGroup,其addView()方法集不受支持。我敢肯定你记得在方法中使用过这一行getView()

convertView = inflater.inflate(R.layout.row_layout, parent, false);

此行确保膨胀R.layout.row_layout文件在其 root 上具有正确LayoutParams的子类集。如果您不这样做,如果根目录是. 也有一些特殊和重要的,你应该确保其中的观点是正确的。AdapterViewViewGroupRelativeLayoutTableLayout/TableRowLayoutParamsLayoutParams

于 2012-09-24T15:47:41.600 回答
21

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

家长:

在这种情况下,是围绕要使用 findViewById() 膨胀的视图对象的小部件/布局。

附加到根:

将视图附加到它们的父视图(包括它们在父层次结构中),因此视图接收到的任何触摸事件也将被转移到父视图。现在取决于父母是想娱乐这些事件还是忽略它们。如果设置为 false,它们不会作为父级的直接子级添加,并且父级不会从视图中接收任何触摸事件。

希望这能消除混乱

于 2014-03-24T16:27:47.877 回答
21

我写了这个答案,因为即使在浏览了几个 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>

按钮.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(View view)方法,可用于将视图添加到 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: The specified child has a parent。由于片段布局已经在 add() 方法中添加到父布局中。

当你膨胀 Fragments 时,你应该总是为 attachToRoot 传递 false。添加、删除和替换 Fragment 是 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)
于 2017-10-10T17:52:27.393 回答
10

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

一般来说,如果attachToRoot设置为true,那么第一个参数中指定的布局文件会在那个时刻被膨胀并附加到第二个参数中指定的ViewGroup。当 attachToRoot 为 false 时,第一个参数中的布局文件会膨胀并作为视图返回,并且任何视图附件都会在其他时间发生。

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

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

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

于 2016-02-23T17:01:52.320 回答
4

attachToRoot设置为 true 意味着inflatedView将被添加到父视图的层次结构中。因此,用户可能会“看到”并感知触摸事件(或任何其他 UI 操作)。否则,它只是被创建,没有被添加到任何视图层次结构中,因此无法看到或处理触摸事件。

对于刚接触 Android 的 iOS 开发人员,attachToRoot设置为 true 意味着您调用此方法:

[parent addSubview:inflatedView];

如果更进一步,您可能会问:如果我设置attachToRoot为 ,为什么要传递父视图false?这是因为您的 XML 树中的根元素需要父视图来计算一些 LayoutParams(如匹配父级)。

于 2015-12-21T05:59:05.863 回答
0

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

于 2012-09-24T14:58:42.970 回答
0

attachToRoot 设置为真:

如果 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 我们想将它附加到 mLLinearLayout。我们的布局参数得到尊重,因为我们知道 Button 被添加到了 LinearLayout。Button 的布局参数类型应该是 LinearLayout.LayoutParams。

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

如果 attachToRoot 设置为 false,则第一个参数中指定的布局文件会被扩展,并且不会附加到第二个参数中指定的 ViewGroup,但扩展后的视图会获取父级的 LayoutParams,从而使该视图能够正确地适应父级。


让我们看看何时要将 attachToRoot 设置为 false。在这种情况下,inflate()的第一个参数中指定的View此时并没有附加到第二个参数中的ViewGroup。

回想一下我们之前的 Button 示例,我们希望将一个自定义 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 时,错误的 attachToRoot 示例需要更多的工作。

attachToRoot 设置为false(需要false)

在onCreateView() 中膨胀和返回Fragment 的View 时,一定要为attachToRoot 传入false。如果你传入 true,你会得到一个 IllegalStateException 因为指定的孩子已经有一个父母。您应该已经指定了 Fragment 视图将放回 Activity 中的位置。添加、删除和替换 Fragment 是 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 的 View 附加到此 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 中,我们仍应使用父 View 的 LayoutParams 以便新 View 在最终附加时确定其大小和位置。

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

于 2017-06-26T05:50:57.857 回答
0

只是分享我在研究这个主题时遇到的一些观点,

除了接受的答案之外,我还想提出一些可能会有所帮助的观点。

因此,当我将attachToRoot用作 true 时,返回的视图是ViewGroup类型,即作为inflate(layoutResource,ViewGroup,attachToRoot)方法的参数传递的父视图组,不是传递的布局类型,而是在attachToRoot上如果为 false,我们将获得该 layoutResource 的根ViewGroup的函数返回类型。

让我用一个例子来解释:

如果我们有一个LinearLayout作为布局,然后我们想通过inflate函数在其中添加TextView 。

然后使用attachToRoot作为真正的膨胀函数返回一个线性布局类型视图

在使用attachToRoot作为膨胀函数时,返回一个TextView类型的视图

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

于 2020-06-04T09:48:43.727 回答
0

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

RelativeLayout
           ------->LinearLayout

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

image_view_layout.xml

附加到根:

//here container is the LinearLayout

    View v = Inflater.Inflate(R.layout.image_view_layout,container,true);
  1. 这里 v 包含容器布局的引用,即 LinearLayout。如果要设置setImageResource(R.drawable.np);ImageView 之类的参数,则必须通过父级的引用找到它,即view.findById()
  2. v 的父级将是 FrameLayout。
  3. LayoutParams 将属于 FrameLayout。

不附加到根:

//here container is the LinearLayout
    View v = Inflater.Inflate(R.layout.image_view_layout,container,false);
  1. 这里 v 包含无引用容器布局,但直接引用膨胀的 ImageView,因此您可以设置其参数 likeview.setImageResource(R.drawable.np);而无需引用 like findViewById。但是指定了容器,以便 ImageView 获取容器的 LayoutParams,因此您可以说容器的引用仅用于 LayoutParams 没有别的。
  2. 所以在特殊情况下 Parent 将为空。
  3. LayoutParams 将是 LinearLayout。
于 2017-02-28T08:14:21.830 回答