LayoutInflater。膨胀文档对我来说并不完全清楚attachtorroot参数的用途。
attachtorroot:膨胀的层次结构是否应该附加到根参数?如果为false,则root仅用于创建正确的 XML中根视图的LayoutParams的子类。
有没有人能更详细地解释一下,具体地说,根视图是什么,也许还能举个例子,说明在真值和假值之间的行为变化?
LayoutInflater。膨胀文档对我来说并不完全清楚attachtorroot参数的用途。
attachtorroot:膨胀的层次结构是否应该附加到根参数?如果为false,则root仅用于创建正确的 XML中根视图的LayoutParams的子类。
有没有人能更详细地解释一下,具体地说,根视图是什么,也许还能举个例子,说明在真值和假值之间的行为变化?
当前回答
我写这个答案是因为即使在浏览了几个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:
如果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
回复中似乎有很多文本,但没有代码,这就是为什么我决定用一个代码示例来恢复这个老问题,在几个回复中人们提到:
如果设置为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参数为真,并以完全相同的方式分配父布局参数,如果我将使用addView编程,或如果我在xml中这样做:
LinearLayout < > <视图…/ > ... 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);
来指定它将是它自己的根。
文档和之前的两个答案应该足够了,只是我的一些想法。
充气方法用于充气布局文件。有了这些膨胀的布局,你就有可能直接将它们附加到父ViewGroup上,或者只是从那个布局文件中膨胀视图层次结构,并在正常的视图层次结构之外使用它。
在第一种情况下,attachtorroot参数必须设置为true(或者简单地使用膨胀方法,它接受一个布局文件和一个父根ViewGroup(非空))。在这种情况下,返回的视图只是在方法中传递的ViewGroup,膨胀的视图层次结构将添加到该ViewGroup。
For the second option the returned View is the root ViewGroup from the layout file. If you remember our last discussion from the include-merge pair question this is one of the reasons for the merge's limitation(when a layout file with merge as root is inflated, you must supply a parent and attachedToRoot must be set to true). If you had a layout file with the root a merge tag and attachedToRoot was set to false then the inflate method will have nothing to return as merge doesn't have an equivalent. Also, as the documentation says, the inflate version with attachToRoot set to false is important because you can create the view hierarchy with the correct LayoutParams from the parent. This is important in some cases, most notable with the children of AdapterView, a subclass of ViewGroup, for which the addView() methods set is not supported. I'm sure you recall using this line in the getView() method:
convertView = inflater.inflate(R.layout.row_layout, parent, false);
这一行确保了膨胀的R.layout。row_layout文件在它的根ViewGroup上设置了来自AdapterView子类的正确LayoutParams。如果你不这样做,你可能会有一些问题与布局文件,如果根是RelativeLayout。tableelayout /TableRow也有一些特殊和重要的LayoutParams,你应该确保它们中的视图有正确的LayoutParams。
我写这个答案是因为即使在浏览了几个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的真正目的是什么。在研究了一些UI后,我终于得到了答案:
家长:
在本例中是围绕视图对象的小部件/布局,您希望使用findViewById()进行膨胀。
attachToRoot:
将视图附加到它们的父视图(将它们包含在父层次结构中),因此视图接收到的任何触摸事件也将被转移到父视图。现在,由父母决定是要接受这些事件还是忽略它们。如果设置为false,它们不会作为父节点的直接子节点被添加,父节点也不会从视图中接收任何触摸事件。
希望这能澄清困惑