生成的绑定 class 仅将 LayoutInflater 作为其唯一参数

Generated binding class that only takes a LayoutInflater as its sole argument

来自 Android 数据绑定库的 generated binding class 有不同版本的 inflate 方法:一个看起来像通常的 LayoutInflater.inflate 方法,它采用 viewGroupattachToRoot 参数,还有一个只接受 LayoutInflater 而没有其他参数。

文档没有解释两者之间的区别,不幸的是 Android Studio 不允许我逐步查看 inflate 的源代码,这样我才能弄清楚它是哪些值在内部使用 viewGroupattachToRoot。在屏幕上放置片段时,我注意到两者之间没有区别。当我将它用于 RecyclerView 项目时,仅采用 LayoutInflaterinflate 方法无法正确放置项目。

所以我的问题是:此方法在内部使用的 viewGroupattachToRoot 的值是什么?在片段的 onCreateView 中使用它是否合适?

如果您使用的是 Kotlin,仅当 layoutId 事先未知时才使用此版本:

private lateinit var viewModel: MyViewModel

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    val binding : MyFragmentBinding = DataBindingUtil.inflate(inflater, R.layout.my_fragment, container, false)
    viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
    binding.myViewModel = viewModel
    binding.lifecycleOwner = this
    return binding.root
}

否则使用生成的Bindingclass膨胀方法:

lateinit var binding: MyFragmentBinding 
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    val binding = MyFragmentBinding.inflate(inflater, container, false)
    return binding.root
}

更新:

So my question is: What values for viewGroup and attachToRoot does this method use internally and is it appropriate to use it in a fragment's onCreateView?

当您调用 inflate 时仅使用 inflater 作为参数,nullfalse 是传递的默认值 IE。当你这样做时:

val binding: MyFragmentBinding = MyFragmentBinding.inflate(inflater)

这类似于做:

 val binding: MyFragmentBinding = MyFragmentBinding.inflate(inflater, null, false)

What values for viewGroup and attachToRoot does this method use internally?

这些参数反映了 LayoutInflater 上的参数,其中 root: ViewGroup 被描述为

Optional view to be the parent of the generated hierarchy (if attachToRoot is true), or else simply an object that provides a set of LayoutParams values for root of the returned hierarchy (if attachToRoot is false.) This value may be null.

来源:LayoutInflater docs,强调我的。

root 参数用于 generate LayoutParams 来自 layout_* XML 属性的膨胀视图。每个布局可以理解一组不同的布局属性并定义一组不同的默认值。这就是为什么你应该总是通过 root。唯一不可能的地方是在展开对话框视图时。

绑定上的单参数方法不提供 root 布局,因此此时不会为膨胀视图生成 LayoutParams。换句话说,inflate(inflater) == inflate(inflater, null, false).

如果视图没有 LayoutParams 当您附加它时,它的新父布局将使用 generateDefaultLayoutParams.[=51= 生成默认值]

如果您遵循 generateDefaultLayoutParamsRecyclerView 中的操作,it's delegated to the layout manager. Now look at the default LayoutParams provided by LinearLayoutManager

@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
    return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT);
}

通常,如果您打算有一个垂直列表,您会在项目上设置 android:layout_width="match_parent"。但是,当您将没有 LayoutParams 的视图附加到 RecyclerView 时,它会将两个维度都设置为 wrap_content。这就是为什么你的布局在用 root == null.

膨胀时看起来不对的原因

Is it appropriate to use [the one-parameter inflate method] in a fragment's onCreateView?

单参数膨胀器可以用于膨胀对话框视图,其中没有 parent/container/root 视图。

通常,您会将片段附加到 FrameLayoutFragmentContainerView(扩展 FrameLayout)。 FrameLayout 生成 LayoutParams,宽度和高度为 match_parent。如果这对您的片段没问题,您可以在绑定上使用单参数 inflate 方法。

否则始终使用 provided/known 父代生成正确的 LaoyutParams。不要依赖您要附加视图的容器提供的默认 LayoutParams,即使它是相同的布局。您在 XML 中定义了一些布局参数,您希望它们得到尊重。

请记住,如果您持有对绑定的引用,则会涉及一些片段生命周期仪式。

这是我当前的片段 + 视图绑定设置:

// Nullable reference so we can use it later and clear it later.
private var binding: ExampleFragmentBinding? = null

private fun requireBinding() = checkNotNull(binding)

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
    // Respect the XML layout params.
    val binding = ExampleFragmentBinding.inflate(inflater, container, false)
    this.binding = binding
    return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val binding = requireBinding()
    // Do stuff with the views.
}

override fun onDestroyView() {
    // Detach view hierarchy reference from fragment, which may prevent memory leaks.
    this.binding = null

    super.onDestroyView()
}