LayoutInflater attachtorroot参数是什么意思?

LayoutInflater.inflate文档对我来说并不是很清楚attachToRoot参数的目的。

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

. XML中的根视图LayoutParams的子类

有没有人能更详细地解释一下,具体地说根视图是什么,并可能展示一个在truefalse值之间的行为变化的例子?

77054 次浏览

当你定义父对象时,attachtorroot决定你是否想要膨胀器把它附加到父对象上。在某些情况下,这会导致问题,比如在ListAdapter中,它会导致一个异常,因为列表试图将视图添加到列表中,但它说它已经附加。在其他情况下,您只是自己膨胀视图以添加到活动,这可能很方便,并节省了一行代码。

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

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

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

inflate方法用于填充布局文件。有了这些膨胀的布局,你可以直接将它们附加到父类ViewGroup,或者只是从该布局文件中膨胀视图层次结构,并在正常的视图层次结构之外使用它。

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

对于第二个选项,返回的View是布局文件中的根ViewGroup。如果你还记得我们上次关于ViewGroup2的讨论,这是merge限制的原因之一(当一个以merge作为根的布局文件被膨胀时,你必须提供一个父文件,并且attachedToRoot必须设置为true)。如果你有一个带有merge根标记的布局文件,并且attachedToRoot被设置为false,那么ViewGroup0方法将没有任何返回值,因为merge没有等效值。 同样,正如文档所说,将attachToRoot设置为falseinflate版本很重要,因为你可以从父类中使用正确的LayoutParams创建视图层次结构。这在某些情况下很重要,尤其是AdapterView的子类,它是ViewGroup的子类,不支持addView()方法集。我相信你记得在getView()方法中使用这一行:

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

这一行确保膨胀的R.layout.row_layout文件具有来自其根ViewGroup上的AdapterView子类的正确LayoutParams。如果你不这样做,如果根是RelativeLayout,布局文件可能会有一些问题。TableLayout/TableRow也有一些特殊和重要的LayoutParams,你应该确保它们中的视图有正确的LayoutParams

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

如果设置为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参数是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. inflation (r.b ayout.fragment, null, false);

来指定它将是它自己的根。

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

家长:

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

attachToRoot:

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

希望这能澄清困惑

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

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

[parent addSubview:inflatedView];

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

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

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

除非你看到很多例子,否则这可能没有多大意义。当在Fragment的onCreateView方法中调用layoutinflater . expand()时,你需要为attachtorroot传递false,因为与该Fragment关联的Activity实际上负责添加该Fragment的视图。如果您在稍后的某个时间点(例如使用addView()方法)手动扩展视图并将一个视图添加到另一个视图,那么您将希望为attachtorroot传递false,因为附件是在稍后的时间点到达的。

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

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

例如,我们有ImageViewLinearLayoutRelativeLayout。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);
    这里v包含了容器布局的引用,即 LinearLayout。如果你想设置像ImageView的setImageResource(R.drawable.np);这样的参数,你必须通过父类的引用找到它,即view.findById()
  1. v的父元素是FrameLayout。
  2. LayoutParams将属于FrameLayout。

不连接到根:

//here container is the LinearLayout
View v = Inflater.Inflate(R.layout.image_view_layout,container,false);
    这里v包含的不是参考容器布局,而是直接布局 ImageView的引用是膨胀的,所以你可以设置它的 参数如view.setImageResource(R.drawable.np);没有 引用像findViewById。但是容器被指定了 ImageView获取容器的LayoutParams,你可以说 container的引用只是为了LayoutParams什么都没有 李。< / >
  1. 所以在特定情况下Parent将为null。
  2. LayoutParams将属于LinearLayout。

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) < br >

如果attachToRoot设置为false,则第一个参数中指定的布局文件将被膨胀,并且附加到第二个参数中指定的ViewGroup,但膨胀的view获取父节点的LayoutParams使该视图能够正确地适合父视图。

< br >

让我们看看什么时候你想把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示例需要更多的工作。

< p > attachtorroot设置为false(false是必需的) < br > < br > 当在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()方法请求一个根视图组?< br > < br > 事实证明,即使我们没有立即将新膨胀的视图添加到它的父视图组中,我们仍然应该使用父视图的LayoutParams,以便新视图在最终附加时确定它的大小和位置

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

现在与否

“第三个”参数attachtorroot是真还是假的主要区别是这样的。

当你输入attachtorroot

正确的:添加子视图到父视图现在
错误的:将子视图添加到父视图不是现在 稍后再添加。' < / p >

晚些时候是什么时候?

稍后是当你使用for eg parent.addView(childView)

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

. 0的问题
inflater.inflate(child,parent,false);
parent.addView(child);

等于

inflater.inflate(child,parent,true);
< p > 一个大禁忌 < br > 当你不负责将子视图添加到父视图时,永远不要将attachtorroot传递为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。< / p >

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

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

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

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)将具有其布局的片段添加到父布局中。如果我们将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)

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

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

因此,当我使用< em > attachToRoot < / em >为真时,返回的视图类型为< em > ViewGroup < / em >,即父的根ViewGroup,它作为< em >膨胀(layoutResource、ViewGroup attachToRoot) < / em >方法的参数传递,而不是传递的布局类型,而是在< em > attachToRoot < / em >上作为假,我们得到了该layoutResource的根< em > ViewGroup < / em >的函数返回类型。

让我用一个例子来解释:

如果我们有一个LinearLayout < em > < / em >作为< em > < / em >根布局,然后我们想通过< em > < /膨胀em >函数在其中添加< em > TextView < / em >

然后使用< em > attachToRoot < / em >作为真正< em > < / em >膨胀函数返回类型为LinearLayout < em > < / em >< em > < / em >视图

而在使用< em > attachToRoot < / em >作为< em >假< / em >膨胀函数时,返回类型为< em > TextView < / em >< em > < / em >视图

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