对inflater.inflate Android文档的困惑

3
我正在学习来自此链接的片段:http://developer.android.com/guide/components/fragments.html 给出了一段代码:
public static class ExampleFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.example_fragment, container, false);
}

我对attachToRoot参数感到困惑,所以我在Stack Overflow上寻求帮助,并找到了类似问题的好答案。所以我理解的是,如果将其设置为true,则片段会附加到活动的根布局,并从那里派生其布局参数。如果为false,则它将简单地返回充气布局的根部,并像片段的独立视图一样运行(从传入容器中派生布局参数)。
现在,我进一步阅读了有关上述示例的attachToRoot的文档:
一个布尔值,指示在充气期间是否应将充气布局附加到ViewGroup(第二个参数)上。(在这种情况下,这是false,因为系统已经将充气布局插入到容器中-传递true会在最终布局中创建多余的视图组。)
我不明白最后一个括号语句的含义,即它应该是false,因为我们已经将布局插入容器中。如果参数为true,最终布局如何具有冗余的视图组。举个例子来阐述这一点将是一个很好的帮助。谢谢。
5个回答

6

我通常不回答自己的问题,但是在为此做更多研究后,我认为这可能会帮助其他人。尽管Marcin的答案是正确的,但我将详细回答一下。

根据代码:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.example_fragment, container, false);
}

第二个参数container是一个帧布局,具有id fragment_container,该活动使用它将片段添加到其布局中。

现在,如果我们深入了解LayoutInflater类的inflate方法,这是代码(我只突出显示相关代码而不是整个代码):

// The view that would be returned from this method.
View result = root;

// Temp is the root view that was found in the xml.                     
final View temp = createViewFromTag(root, name, attrs, false);

首先,它从提供的根创建一个临时视图。
如果 attachToRoot 是 true,那么它会这样做:
if (root != null && attachToRoot) {
    root.addView(temp, params);
}

它将上面创建的临时视图添加到根视图(即容器)中。

如果attachToRoot为false,则会执行以下操作:

if (root == null || !attachToRoot) {
   result = temp;   
}

显而易见,如果 attachToRoot 为 true,则它只需将临时视图添加到根视图(在此示例中为 example_fragment 中的 Root view),然后返回根视图(即 fragment_container,即活动用于放置片段的 id)。如果 attachToRoot 为 false,则它只返回片段 xml 的根视图,即容器参数仅用于获取片段根视图的布局参数(因为它没有根视图,所以需要从其他地方获取参数)。
当 attachToRoot 为 true 时,上面的示例存在问题,因为返回值是带有已添加视图 temp 的根视图(fragment_container 默认已经有一个父级)。现在,如果您尝试进行片段事务,则正在尝试将子视图 fragment_container(已经有父级)添加到另一个 xml(您定义的 framelayout 以添加片段)中。
由于这个原因,Android 抛出以下异常:
if (child.getParent() != null) {
            throw new IllegalStateException("The specified child already has a parent. " + 
"You must call removeView() on the child's parent first.");
    }

将参数设置为true并返回时的问题是,返回的视图已经有一个父级,因此无法在其他地方使用。另一种方法是在onCreateView中创建一个单独的视图组(例如LinearLayout),将参数设置为true,并返回该视图。这样做可以正常工作,因为视图组没有现有的父级。

这是我对上述问题的理解,如果有错误,希望任何Android专家纠正。


精彩的观察! - bks4line
谢谢!这真的很令人困惑...他们应该用一个不那么混乱的方式来命名参数。在我看来,他们应该将参数重命名为inflate(resource, parent, attachToParent),而不是inflate(resource, root, attachToRoot),因为后者只是含糊不清且重复提到。 - Rei

4
这意味着即使在onCreateView()返回View的情况下,它也将被附加到容器视图中,因此在onCreateView()中将其设置为true会导致它被添加两次到容器布局中,这通常不是您想要的。而将根视图设置为非空并仍然将attachToRoot设置为false,则允许充气的视图从根派生,而不被添加。

嗨Marcin,你能告诉我任何我们需要将其设置为true的场景吗?任何小例子都将会有所帮助。 - gaurav jain
例如,您可以从许多布局构建视图。然后,您可以单独填充它们,然后手动添加addView(),或者只需将此标志设置为true,无需再担心。 - Marcin Orlowski
@gauravjain 这是否回答了你的问题? - Marcin Orlowski
请熟悉以下元帖:https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work/5235#5235。 - Marcin Orlowski
1
你可以说这有点简短,或者我需要一些时间来理解这些东西。当我看到源代码时,我真的能够理解底层发生了什么,以及当该参数为true时导致冗余和异常的原因。但是,正如我所说,我很感激你的回答,因为这是唯一一个合法的答案,直到Elitz写下他的答案。此外,感谢上面的链接,但并不是我不接受任何问题的答案。 - gaurav jain
显示剩余2条评论

1

检查这个

View v =getLayoutInflater.inflate(R.layout.example_fragment, viewgroup);

并且还有这个。
View v =getLayoutInflater.inflate(R.layout.example_fragment, null);

现在,如果你第一次调用这个场景。
Log.v("testing",String.valueOf(v.getParent() == null));

当你在第一行检查时,输出为false,但是在第二行检查时输出为true

简单的理解是,如果你指定了一个父级(ViewGroup),它会附加到它上面,但是如果你添加最后一个参数,你告诉解析器跳过View的附加,因此返回的View没有父级,但是如果你指定为null,谁会被附加?没有人,所以返回的View没有父级,正如你所说,它会派生View的layoutParameters。

如果你不添加最后一个参数,View将具有Activity View作为其父级,因此在OncreateView中返回之前已经有父级,这将导致一些异常,因为View不能有不同的父级,Fragment将无法管理View本身。

如果添加true,则从oncreateView返回null


-2
从片段的onCreateView()方法返回的视图将由系统附加到片段的容器中。因此,在这种情况下,inflate()方法的第三个参数应该是false。如果您将其设置为true,则它将被附加到根两次。因此,在这种情况下,您应该将其设置为false。
现在,充气的视图从inflate()方法的第二个参数派生其布局参数,而不是第三个布尔参数。

-2

来自Android官方文档的确认Android documentation

什么是Fragment?

Fragment代表Activity中的一部分用户界面或行为。您可以在单个Activity中组合多个Fragment以构建多窗格UI,并在多个Activity中重复使用Fragment。您可以将Fragment视为Activity的模块化部分,它具有自己的生命周期,接收自己的输入事件,并且您可以在Activity运行时添加或删除它(类似于可以在不同Activity中重复使用的“子Activity”)。

如何在Activity中使用Fragment?

Fragment必须始终嵌入在Activity中,Fragment的生命周期直接受到宿主Activity生命周期的影响。例如,当Activity暂停时,其中所有的Fragment也会暂停,当Activity被销毁时,其中所有的Fragment也会被销毁。但是,在Activity正在运行时(处于恢复的生命周期状态),您可以独立地操作每个Fragment,例如添加或删除它们。

什么是Fragment事务?

你可以独立地操纵每个片段,比如添加或移除它们。当你执行这样的片段事务时,你还可以将其添加到由活动管理的后退栈中 - 活动中的每个后退栈条目都是发生的片段事务的记录。后退栈允许用户通过按下返回按钮来撤销片段事务(向后导航)。

片段和活动之间如何相互交互

当你将一个片段作为活动布局的一部分添加进去时,它存在于活动视图层次结构内的一个ViewGroup中,而片段定义了自己的视图布局。你可以通过在活动的布局文件中声明片段元素,或者通过将其添加到现有的ViewGroup中来将片段插入到活动布局中。

片段生命周期方法 onCreateView()      onCreate()      onPause()

你的问题是

如果attachToRoot参数为true,那么我们已经将它插入到容器中意味着什么?如果参数为true,最终的布局会有多余的ViewGroup吗?

这里是答案:

假设您已经在活动布局中添加了一个片段,那么您就不需要将最后一个参数设置为true。因为每次执行片段的添加或删除时都会进行片段事务。

但是,如果您尚未在布局中添加片段,则可以将其设置为true。

请阅读文档:

您可以通过在活动的布局文件中声明片段(作为元素)或通过将其添加到现有ViewGroup中的应用程序代码来将片段插入到活动布局中。但是,片段不是活动布局的一部分;您还可以使用没有自己UI的片段作为活动的不可见工作者。

所有片段事务都添加到活动返回堆栈中。


我认为你并没有完全理解我的问题。这与我如何将片段静态或动态添加到活动无关。那很清楚,问题在于attachToRoot参数。根据你对动态添加片段到活动的回答,我们应该将它设置为true,但这并不完全正确(会抛出异常)。不管怎样,我感谢你的努力和时间。 - gaurav jain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接