LayoutInflater attachToRoot参数是什么意思?
LayoutInflater.inflate
文档并不完全清楚attachToRoot
参数的用途。
attachToRoot :膨胀的层次结构是否应该附加到根参数? 如果为false,则root仅用于为XML中的根视图创buildLayoutParams的正确子类。
有人可以请详细解释一下,特别是根视图是什么,也可以显示true
值和false
值之间的行为变化的例子?
如果设置为true,那么当您的布局充气时,它将自动添加到第二个参数中指定的ViewGroup的视图层次结构中作为子项。 例如,如果root参数是一个LinearLayout
那么你的充气视图将被自动添加为该视图的一个子视图。
如果它设置为false,那么你的布局将被夸大,但不会被附加到任何其他布局(所以它不会被绘制,接收触摸事件等)。
文档和前两个答案应该足够了,只是我的一些想法。
inflate
方法用于膨胀布局文件。 有了这些膨胀的布局,你可以直接将它们附加到父ViewGroup
或者只是从该布局文件中ViewGroup
视图层次结构,并在普通视图层次结构之外使用它。
在第一种情况下, attachToRoot
参数必须设置为true
(或者使用带布局文件和父根ViewGroup
(非null
)的inflate
方法)。 在这种情况下,返回的View
就是在方法中传递的ViewGroup
将向其中添加充气视图层次结构的ViewGroup
。
对于第二个选项,返回的View
是布局文件中的根ViewGroup
。 如果您还记得我们上一次来自include-merge
对问题的讨论,这是merge
的限制原因之一(当以root身份merge
的布局文件被夸大时,您必须提供一个父级,并且attachedToRoot
必须设置为true
) 。 如果你有一个根merge
标签的布局文件,并且attachedToRoot
被设置为false
那么inflate
方法将没有任何东西可以返回,因为merge
没有等价的东西。 另外,正如文档所述,将attachToRoot
设置为false
的inflate
版本非常重要,因为您可以使用父级的正确LayoutParams
创build视图层次结构。 在某些情况下,这是非常重要的,最常见的是AdapterView
的子类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
param为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
一部分,但是一会儿它就是独立的(无父对象)视图。
碎片也会发生同样的事情,您可以将它们添加到已经存在的组中,并成为它的一部分,或者只传递参数:
inflater.inflate(R.layout.fragment,null,false);
指定它将是它自己的根。
我自己也对attachToRoot
在inflate
方法中的真正用途感到困惑。 经过一番UI学习,我终于得到了答案:
父:
在这种情况下是围绕使用findViewById()进行膨胀的视图对象的小部件/布局。
attachToRoot:
将视图附加到它们的父级(将它们包括在父级层级中),所以视图接收到的任何触摸事件也将被转移到父级视图。 无论是想要接受这些事件还是忽视它,现在都是父母的责任。 如果设置为false,则不会将其添加为父级的直接子级,并且父级也不会从视图中接收任何触摸事件。
希望这个清除混乱
由于inflate()方法的文档,这个主题有很多的困惑。
一般情况下,如果attachToRoot设置为true,则第一个参数中指定的布局文件将被充满并附加到当时第二个参数中指定的ViewGroup。 当attachToRoot为false时,来自第一个参数的布局文件将被充满并返回,因为View和任何View都会在其他时间发生。
除非你看到很多例子,否则这可能没有多大意义。 当在一个Fragment的onCreateView方法内部调用LayoutInflater.inflate()时,你会想为attachToRoot传入false,因为与该Fragment相关联的Activity实际上负责添加该Fragment的视图。 如果在稍后的某个时间点(例如使用addView()方法)手动膨胀并向另一个视图添加View,则需要为attachToRoot传递false,因为附件是在稍后的时间点传入的。
你可以阅读关于对话框和自定义视图的其他几个独特的例子,在我写的关于这个话题的博客文章中。
https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/
attachToRoot
设置为true意味着inflatedView
将被添加到父视图的层次结构中。 因此可能被用户“看到”并感知触摸事件(或任何其他UI操作)。 否则,它只是被创build,没有被添加到任何视图层次结构,因此无法看到或处理触摸事件。
对于attachToRoot
Android的iOS开发人员,将attachToRoot
设置为true表示您调用此方法:
[parent addSubview:inflatedView];
如果进一步您可能会问:为什么我应该通过父视图,如果我将attachToRoot
设置为false
? 这是因为您的XML树中的根元素需要父视图来计算一些LayoutParams(如匹配父项)。
我写了这个答案,因为即使经过几个StackOverflow页面,我不能清楚地知道什么attachToRoot的意思。 下面是LayoutInflater类中的inflate()方法。
View inflate (int resource, ViewGroup root, boolean attachToRoot)
看一下activity_main.xml文件, button.xml布局和我创build的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); }
当我们运行代码时,我们不会在布局中看到button。 这是因为我们的button布局没有添加到主活动布局中,因为attachToRoot设置为false。
LinearLayout有一个addView(视图视图)方法,可用于将视图添加到LinearLayout。 这会将button布局添加到主活动布局中,并在运行代码时使button可见。
root.addView(view);
让我们删除前一行,看看当我们将attachToRoot设置为true时会发生什么。
View view = inflater.inflate(R.layout.button, root, true);
我们再次看到button布局是可见的。 这是因为attachToRoot直接将膨胀的布局附加到指定的父级。 在这种情况下是LinearLayout的根。 在这里,我们不必像前面的例子中那样手动添加视图,而是使用addView(View view)方法。
为什么人们在为片段设置attachToRoot为true时会出现IllegalStateExceptionexception。
这是因为对于片段,您已经指定将片段布局放置在活动文件中的位置。
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .add(R.id.root, fragment) .commit();
add(int parent,Fragment fragment)将具有布局的片段添加到父布局。 如果我们将attachToRoot设置为true,则会得到IllegalStateException:指定的子项已经有父项。 由于片段布局已经添加到add()方法中的父布局。
当你给Fragments充气时,你应该总是传递false给attachToRoot。 FragmentManager的工作是添加,删除和replace碎片。
回到我的例子。 如果我们两个都做
View view = inflater.inflate(R.layout.button, root, true); root.addView(view);
在第一行中,LayoutInflater将button布局附加到根布局,并返回一个包含相同button布局的View对象。 在第二行中,我们将相同的View对象添加到父级根布局。 这会导致与我们在Fragments中看到的相同的IllegalStateException(指定的子项已经有父项)。
请记住,还有另一个重载的inflate()方法,默认情况下它将attachToRoot设置为true。
View inflate (int resource, ViewGroup root)
现在或不现在
“第三个”参数attachToRoot是真或假的主要区别是这个。
当你把attachToRoot
true:将子视图添加到父RIGHT NOW
false:将子视图添加到父母NOT NOW 。
稍后添加。 `
以后什么时候?
以后是当你使用例如parent.addView(childView)
一个常见的误解是,如果attachToRoot参数为false,那么子视图将不会被添加到父级。 错误
在这两种情况下,子视图将被添加到parentView。 这只是时间问题 。
inflater.inflate(child,parent,false); parent.addView(child);
相当于
inflater.inflate(child,parent,true);
一个大的NO- NO
如果您不负责将子视图添加到父级,则不应将attachToRoot传递为true。
例如,当添加片段
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 }
如果你传递第三个参数为true,你将会因为这个人而得到IllegalStateException。
getSupportFragmentManager() .beginTransaction() .add(parent, childFragment) .commit();
由于您已经在onCreateView()中错误地添加了子片段。 调用添加会告诉你,子视图已经添加到父,因此IllegalStateException 。
这里你不负责添加childView,FragmentManager负责。 所以在这种情况下总是传错。
注:我也读过,如果attachToRoot为false,parentView将不会得到childView touchEvents。 但我还没有testing过。
当您定义父级时,attachToRoot将确定您是否希望inflater将其实际附加到父级。 在某些情况下,这会导致问题,就像在ListAdapter中一样,它会导致exception,因为列表试图将视图添加到列表中,但是它表示已经连接。 在其他情况下,您只需将自己的视图添加到活动中,便可以方便地为您节省一行代码。
例如,我们有一个ImageView
,一个LinearLayout
和一个RelativeLayout
。 LinearLayout是RelativeLayout的子项。 View Hierarchy将会是。
RelativeLayout ------->LinearLayout
我们有一个单独的ImageView布局文件
image_view_layout.xml
附加到root:
//here container is the LinearLayout View v = Inflater.Inflate(R.layout.image_view_layout,container,true);
- 这里v包含容器布局的引用,即LinearLayout.and如果你想设置像
setImageResource(R.drawable.np);
这样的参数setImageResource(R.drawable.np);
的ImageView你必须find它的父母的引用即view.findById()
- v的父项将是FrameLayout。
- 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,所以你可以说,容器的引用只是为LayoutParams没有别的。 - 所以在特殊情况下,Parent将为空。
- LayoutParams将是LinearLayout。
attachToRoot设置为true:
如果attachToRoot设置为true,则第一个参数中指定的布局文件将被充满并附加到第二个参数中指定的ViewGroup。
想象一下,我们在一个XML布局文件中指定了一个button,其布局宽度和布局高度都设置为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>
我们现在想以编程方式将此Button添加到片段或Activity中的LinearLayout中。 如果我们的LinearLayout已经是一个成员variables,mLinearLayout,我们可以简单的添加下面的button:
inflater.inflate(R.layout.custom_button, mLinearLayout, true);
我们指定我们要从它的布局资源文件中充满Button。 然后我们告诉LayoutInflater我们要把它附加到mLinearLayout。 我们的布局参数被尊敬,因为我们知道Button被添加到LinearLayout。 button的布局参数types应该是LinearLayout.LayoutParams。
attachToRoot设置为false(不要求使用false)
如果attachToRoot设置为false,那么在第一个参数中指定的布局文件将被膨胀,而不会附加到在第二个参数中指定的ViewGroup,但是膨胀视图会获取父级的LayoutParams ,从而使该视图能够在父级中正确匹配。
让我们来看看什么时候你想把attachToRoot设置为false。 在这种情况下,inflate()的第一个参数中指定的视图在此时未附加到第二个参数中的ViewGroup。
回想一下前面我们的button示例,我们想要将一个自定义button从布局文件附加到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()方法。
当我们手动将视图添加到ViewGroup时,假attachToRoot示例需要更多的工作。
attachToRoot设置为false(false是必需的)
当在onCreateView()中膨胀并返回片段视图时,请务必为attachToRoot传入false。 如果你传入true,你将得到一个IllegalStateException,因为指定的子项已经有一个父项。 你应该已经指定了你的片段的视图将放回到你的活动中。 FragmentManager的工作是添加,删除和replace碎片。
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(); }
在你的Activity中保存你的Fragment的root_viewGroup容器是在你的Fragment的onCreateView()中给你的ViewGroup参数。 这也是您传递给LayoutInflater.inflate()的ViewGroup。 然而,FragmentManager将处理你的Fragment的视图到这个ViewGroup。 你不想附加两次。 将attachToRoot设置为false。
public View onCreateView(LayoutInflater inflater, ViewGroup parentViewGroup, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_layout, parentViewGroup, false); … return view; }
如果我们不想在onCreateView()中附加它,为什么我们首先给了我们的Fragment的父ViewGroup呢? 为什么inflate()方法请求一个根ViewGroup?
事实certificate,即使我们没有立即把我们新增的View添加到它的父ViewGroup中,我们仍然应该使用父级的LayoutParams来让新的View在最终连接时确定它的大小和位置。
链接: https : //youtu.be/1Y0LlmTCOkM?t = 409