LayoutInflater。膨胀文档对我来说并不完全清楚attachtorroot参数的用途。

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

有没有人能更详细地解释一下,具体地说,根视图是什么,也许还能举个例子,说明在真值和假值之间的行为变化?


当前回答

我自己也很困惑膨胀方法中attachtorroot的真正目的是什么。在研究了一些UI后,我终于得到了答案:

家长:

在本例中是围绕视图对象的小部件/布局,您希望使用findViewById()进行膨胀。

attachToRoot:

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

希望这能澄清困惑

其他回答

我写这个答案是因为即使在浏览了几个StackOverflow页面后,我也不能清楚地理解attachtorroot的意思。下面是LayoutInflater类中的inflation()方法。

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);
}

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

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

root.addView(view);

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

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

我们再次看到按钮布局是可见的。这是因为attachtorroot直接将膨胀布局附加到指定的父布局上。这里是根LinearLayout。在这里,我们不需要像之前使用addView(View View)方法那样手动添加视图。

为什么人们得到IllegalStateException当设置一个片段的attachtorroot为真。

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

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

add(int parent, Fragment Fragment)将拥有自己布局的片段添加到父布局中。如果我们将attachtorroot设置为true,你将得到IllegalStateException:指定的子已经有一个父。因为片段布局已经在add()方法中添加到父布局中。

当你在膨胀fragment时,你应该总是为attachtorroot传递false。添加、删除和替换片段是FragmentManager的工作。

回到我的例子。如果我们两者都做呢?

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

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

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

View inflate (int resource, ViewGroup root)

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

对于刚接触Android的iOS开发者,attachtorroot设置为true意味着你调用这个方法:

[parent addSubview:inflatedView];

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

只是分享一些我在研究这个话题时遇到的问题,

除了公认的答案,我想提出一些可能会有所帮助的观点。

所以,当我使用attachtorroot为true时,返回的视图类型为ViewGroup,即父的根ViewGroup,它作为参数传递给了填充(layoutResource,ViewGroup, attachtorroot)方法,而不是传递的布局类型,但在attachtorroot为false时,我们得到了该layoutResource的根ViewGroup的函数返回类型。

让我用一个例子来解释:

如果我们有一个线性布局作为根布局,然后我们要添加TextView在它通过膨胀函数。

然后使用attachtorroot作为真正的膨胀函数返回一个LinearLayout类型的视图

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

希望这一发现能有所帮助……

由于inflation()方法的文档,关于这个主题有很多困惑。

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

This probably doesn't mean much unless you see a lot of examples. When calling LayoutInflater.inflate() inside of the onCreateView method of a Fragment, you will want to pass in false for attachToRoot because the Activity associated with that Fragment is actually responsible for adding that Fragment's view. If you are manually inflating and adding a View to another View at some later point in time, such as with the addView() method, you will want to pass in false for attachToRoot because the attachment comes at a later point in time.

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

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

attachtorroot设置为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>

我们现在想通过编程的方式将这个按钮添加到Fragment或Activity中的LinearLayout中。如果我们的LinearLayout已经是一个成员变量mLinearLayout,我们可以简单地添加下面的按钮:

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

我们指定我们想要从它的布局资源文件中膨胀Button;然后我们告诉LayoutInflater我们想要将它附加到mLinearLayout。我们的布局参数被尊重,因为我们知道按钮被添加到线性布局。按钮的布局参数类型应该是LinearLayout.LayoutParams。

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

如果attachToRoot设置为false,那么第一个参数中指定的布局文件将被膨胀,并且不会附加到第二个参数中指定的ViewGroup,但是这个膨胀的视图将获得父视图的LayoutParams,这使得该视图能够正确地适应父视图。

让我们看看什么时候你想把attachtorroot设为false。在这个场景中,在inflation()的第一个参数中指定的View此时还没有附加到第二个参数中的ViewGroup。

回想一下前面的Button例子,我们想从布局文件中附加一个自定义Button到mLinearLayout。我们仍然可以通过为attachtorroot传递false来将我们的Button附加到mLinearLayout -我们只是随后自己手动添加它。

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

这两行代码等价于我们在前面为attachtorroot传递true时在一行代码中所写的内容。通过传入false,我们说我们还不想将我们的视图附加到根ViewGroup。我们说它会在其他时间点发生。在本例中,另一个时间点就是紧接在inflation下面使用的addView()方法。

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

attachtorroot设置为false(false是必需的) 当在onCreateView()中膨胀并返回一个片段的视图时,请确保为attachtorroot传入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();
}

root_viewGroup容器将在你的活动中保存你的片段是在你的片段中的onCreateView()中给你的ViewGroup参数。它也是你传递给layoutinflater . inflation()的ViewGroup。然而,FragmentManager将处理将你的片段视图附加到这个ViewGroup。您不希望将它附加两次。设置attachtorroot为false。

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

为什么我们给我们的片段的父ViewGroup在第一个地方,如果我们不想附加在onCreateView()?为什么inflation()方法请求一个根视图组? 事实证明,即使我们没有立即将新膨胀的视图添加到它的父视图组中,我们仍然应该使用父视图的LayoutParams,以便新视图在最终附加时确定它的大小和位置。

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