导航组件:如何在每个片段中设置带有工具栏的抽屉

问题描述:

我正在使用导航组件,并希望将抽屉布局与每个片段中的工具栏连接起来,而不是活动.

I'm using navigation component and want to connect drawer layout with toolbar in each fragment, not the activity.

我尝试了这个在 onViewCreated() 上调用的 answer,但从活动中引用的任何视图都是空的.我猜这是因为片段在从活动的 onCreate 方法返回之前在布局中膨胀了.

I tried this answer which is called on onViewCreated() but any view referenced from the activity is null. I guess it's because fragment is inflated in the layout before returning from the activity's onCreate method.

我使用这个扩展函数将抽屉与片段的工具栏连接起来,我试图从 onCreateView() 和 onViewCreated() 调用它,但没有工作,并且活动的抽屉布局始终为空.我只在从 onStart() 调用它时才工作,但我认为这不是正确的方法:

I use this extension function to connect the drawer with the fragment's toolbar, I tried to call it from onCreateView() and onViewCreated() but did't work and the activity's drawer layout is always null. I works only if it's called from onStart() but I don't think it's the right way:

private fun AppCompatActivity.setToolbar() {
    setSupportActionBar(binding.toolbar)
    setHasOptionsMenu(true)
    val drawer = findViewById<DrawerLayout>(R.id.drawer)
    binding.toolbar.setupWithNavController(findNavController(), drawer)
}

调用此函数的正确位置是什么?

What's the right place to call this function?

当您调用 setContentView(R.id.activity_layout) 时,整个视图层次结构首先被膨胀,然后附加到 Activity.只有在 setContentView() 返回后,findViewById() 才会找到任何新膨胀的视图.

When you call setContentView(R.id.activity_layout), the entire view hierarchy is first inflated, then attached to the Activity. It is only after setContentView() returns that findViewById() will find any of the newly inflated views.

当您使用 <fragment> 标记时,Fragment 的视图及其所有子 Fragment 的视图将作为该通胀调用的一部分同步创建.这意味着 setContentView()onCreateView()onViewCreated() 方法被调用时还没有完成.这就是调用 findViewById() 返回 null 的原因 - 活动的视图实际上尚未完成创建.

When you use the <fragment> tag, the Fragment's view and the views of all of its child fragments are synchronously created as part of that inflation call. This means that setContentView() has not completed by the time the onCreateView() and onViewCreated() methods are called. This is why that calling findViewById() returns null - the activity's view hasn't actually finished being created.

FragmentContainerView 是专门为避免这些特殊情况而构建的,而是使用与其他片段相同的机制——也就是说,它只使用普通的 FragmentTransaction 来添加你的片段——就像您自己在 onCreate() 方法中调用 beginTransaction()+commitNow() 一样.这意味着 Fragment 不会被强制同步创建其视图作为 setContentView() 的一部分,而是可以与其他每个 Fragment after setContentView() 返回.这就是允许 onCreateView()onViewCreated() 中的 findViewById() 工作的原因.

FragmentContainerView was specifically built to avoid these special cases and instead use the same mechanisms as other fragments - namely, it just uses a normal FragmentTransaction to add your Fragment - the same as if you called beginTransaction()+commitNow() in your onCreate() method yourself. This means that the Fragment is not forced to synchronously create its view as part of setContentView(), but can do it alongside every other Fragment after setContentView() returns. This is what allows findViewById() from onCreateView() or onViewCreated() work.