2012-09-24 83 views
113

LayoutInflater.inflate文档对我来说不完全清楚attachToRoot参数的用途。LayoutInflater attachToRoot参数的含义是什么?

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

可能有人请解释一下,特别是根的观点是什么,也许表明truefalse值之间的行为变化的一个例子吗?

+1

相关:[制作LayoutInflater感](http://stackoverflow.com/questions/5026926/making-sense-of-layoutinflater) – blahdiblah

+0

重复:https://stackoverflow.com/questions/22326314/什么是使用附着到根布局的inflater – mikebabcock

回答

1

当您定义父级时,attachToRoot将确定您是否希望inflater将其实际附加到父级或不是父级。在某些情况下,这会导致问题,就像在ListAdapter中一样,它将导致异常,因为列表试图将视图添加到列表中,但它表示它已经连接。在其他情况下,您只需将视图自己添加到Activity中就可以很方便并为您节省一行代码。

66

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

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

+10

我很困惑。直到我阅读[这个答案](http://stackoverflow.com/a/6036484/403455)时,我得到了“指定的孩子已经有一个父错误”,它指示我在我的“attachToRoot”期间使用'false'片段的'onCreateView'。这解决了问题,但片段的布局是可见的和活跃的,尽管你的答案。这是怎么回事? –

+52

因为片段自动附加从onCreateView返回的布局,所以如果你手动附加onCreateView然后你查看会附加到2个父母(这会产生你提到的错误) –

+8

我在这里有点困惑,@JosephEarl你说如果设置为'true',视图被附加到'容器'的第二参数,但是那么你说片段是从'onCreateView()'自动附加的,所以就我的理解而言,第三个参数是无用的,应该总是设置为'false'? – unmultimedio

25

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

inflate方法用于给布局文件充气。使用这些膨胀的布局,您必须可以将它们直接附加到父项ViewGroup,或者只是从该布局文件中膨胀视图层次结构,并在普通视图层次结构外使用它。

在第一种情况的attachToRoot参数将必须被设置为true(或许多简单的采用了inflate方法,它的布局文件和母根ViewGroup(非null))。在这种情况下,返回的View只是在方法中传递的ViewGroupViewGroup将向其添加充值视图层次结构。

对于第二个选项,返回的View是布局文件中的根ViewGroup。如果您还记得include-merge pair question的最后一次讨论,这是merge的局限性的原因之一(以merge作为根的布局文件被夸大时,您必须提供父项并且attachedToRoot必须设置为true)。如果您有一个布局文件,其根目录为merge标记,并且attachedToRoot设置为false,则inflate方法将无法返回,因为merge没有等效项。 另外,如文档所述,inflate版本的attachToRoot设置为false非常重要,因为您可以使用父级的正确LayoutParams创建视图层次结构。这在一些情况下是重要的,其中最值得注意的是AdapterView的子类ViewGroup的子类,其中addView()方法组不受支持。我敢肯定,你还记得使用该线路getView()方法:

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

该行确保膨胀R.layout.row_layout文件具有从AdapterView子在其根ViewGroup设置正确的LayoutParams。如果你不这样做,如果根目录是RelativeLayout,你可能会在布局文件中遇到一些问题。 TableLayout/TableRow也有一些特殊和重要的LayoutParams,你应该确保其中的视图有正确的LayoutParams

33

好像很多的反应,但没有代码文本的,这就是为什么我决定恢复同一个代码示例这个老问题,在若干答复人提到:

如果设置为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> 

下面的代码说明路过的时候attachRootfalse场景:

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,空,假的);

指定它将是它自己的根。

+1

最重要的是,这是最有帮助的。 –

16

我自己也对attachToRootinflate方法中的真实目的感到困惑。有点UI的学习后,我终于得到了答案:

父:

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

attachToRoot:

附加意见,它们的母体(包括它们在父层级),所以任何触摸事件,该观点收到也将被转移到父视图。无论是想要接受这些事件还是忽视它们,现在都取决于父母。如果设置为false,则不会将其添加为父级的直接子级,并且父级也不会从视图中接收任何触摸事件。

希望这将清除混乱

+0

你的答案已经在这里提供:http://stackoverflow.com/questions/22326314/what-is-the-use-of-attach-to-root-in-layout-inflater –

4

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

对于iOS开发新到Android,attachToRoot设置为true意味着你调用这个方法:

[parent addSubview:inflatedView]; 

如果要进一步的你可能会问:为什么我应该通过,如果我设置attachToRootfalse父视图?这是因为您的XML树中的根元素需要父视图来计算一些LayoutParams(如匹配父项)。

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/

0

例如,我们有一个ImageView,一个LinearLayoutRelativeLayout。 LinearLayout是RelativeLayout的子项。 View Hierarchy会。

RelativeLayout 
      ------->LinearLayout 

,我们有ImageView的

image_view_layout一个单独的布局文件。XML

附加到根:

//here container is the LinearLayout 

    View v = Inflater.Inflate(R.layout.image_view_layout,container,true); 
  1. 这里五载容器布局即 参考LinearLayout.and如果你想设置的参数,如的ImageView的setImageResource(R.drawable.np);你会必须通过父亲的参考找到它,例如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. 这里五载无参考容器的布局,但直接 参考该充气,所以你可以设置像view.setImageResource(R.drawable.np);其 参数没有ImageView的 参考findViewById。但容器被指定为使得ImageView获取容器的LayoutParams,因此您可以说 容器的引用仅用于LayoutParams,其他都不用 。
  2. 所以在特殊情况下,Parent将为空。
  3. LayoutParams将为LinearLayout。
+0

需要更多的解释,代码应该精心制作 – blackHawk

+0

是的,当然你需要一些词lemme知道:) –

0

attachToRoot设置为true:

如果attachToRoot被设置为真,则在所述第一参数指定的布局文件被充气并连接到在所述第二参数指定的的ViewGroup。

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

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

我们现在想以编程方式将此按钮添加到片段或活动的LinearLayout中。如果我们的LinearLayout已经是一个成员变量,mLinearLayout,我们可以简单地用下面的添加按钮:

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

我们指定了我们想要夸大其布局资源文件的按钮;然后我们告诉LayoutInflater我们要将它附加到mLinearLayout。我们的布局参数很荣幸,因为我们知道Button被添加到LinearLayout中。按钮的布局参数类型应该是LinearLayout.LayoutParams。

attachToRoot设置为false(用假不是必需的)

如果attachToRoot被设置为假,则在所述第一参数指定的布局文件被充气并连接到在第二个参数中指定的ViewGroup,但是虚拟的视图获取父级的LayoutParams,该视图使该视图能够在父级中正确匹配。


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

回想一下前面我们的按钮示例,我们希望将一个自定义按钮从布局文件附加到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(假是必需的)

当充气并返回一个片段在onCreateView(视图),请务必假以通为attachToRoot。如果你传入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.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; 
} 

为什么我们要考虑到我们的片段的父ViewGroup中位居第一,如果我们不希望它附加在onCreateView()?为什么inflate()方法请求一个根ViewGroup?

事实证明,即使我们没有立即将其新的充气View添加到其父ViewGroup中,我们仍然应该使用父级的LayoutParams,以便新View随时随地确定其大小和位置。

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

12

NOW OR NOT NOW

“第三” 参数attachToRoot是真的还是假的之间的主要区别是这样的。

当你把attachToRoot

真:添加子视图父现在
假:添加子视图父不是现在
稍后添加。 `

什么时候是以后

,后来是当你使用如parent.addView(childView)

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

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

相当于

inflater.inflate(child,parent,true); 

大的NO-NO
切勿将attachToRoot当你不负责添加子视图父为真。
例如,当将片段

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

如果传递第三个参数为真,你会得到,因为这家伙IllegalStateException异常。

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

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

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

3

我写了这个答案,因为即使通过几个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(View view)可用于将视图添加到LinearLayout的方法。这会将按钮布局添加到主活动布局中,并在运行代码时使按钮可见。

root.addView(view); 

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

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

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

为什么人们在为片段设置attachToRoot为true时会出现IllegalStateException异常。

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

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

加载(INT父,片段片段)增加,其具有它的布局与母体布局的片段。如果我们将attachToRoot设置为true,则会得到IllegalStateException:指定的子项已经有父项。由于片段布局已添加到add()方法中的父布局中。

当你给片段充气时,你总是应该为attachToRoot传递false。 FragmentManager的工作是添加,删除和替换碎片。

回到我的例子。如果我们都这样做了会怎样

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

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

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

View inflate (int resource, ViewGroup root) 
相关问题