提问人:Haeiny 提问时间:9/4/2023 更新时间:9/8/2023 访问量:62
如何为每个片段应用不同的工具栏(actionbar)布局?
How to apply different toolbar(actionbar) layout for each fragment?
问:
我正在应用单活动架构,我想使用工具栏使用顶部固定栏。
在我的项目中,工具栏的形式因片段而异,形式如下:
在 MainFragment 和一两个 Fragment 中,工具栏有一个 drawerLayout 和两个菜单选项,在除此之外的所有 Fragment 中,它有一个后退按钮,没有菜单选项。
但是我不知道如何更改每个片段的工具栏布局。
对于更改工具栏布局,我有两个想法。 第一种方法是使用 Activity xml 的工具栏,并在使用 menuProvider 移动片段时更改代码上的菜单。 喜欢这个:
MainActivity.kt(主活动.kt)
private fun initToolbar() {
setSupportActionBar(binding.content.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_action_open_drawer)
supportActionBar?.setDisplayShowTitleEnabled(false)
binding.content.toolbar.title = "test"
}
private fun initMenuProvider() {
addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.toolbar_activity, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
when (menuItem.itemId) {
android.R.id.home -> { // for open drawerLayout
binding.drawerLayout.openDrawer(GravityCompat.START)
}
}
return true
}
})
}
fun changeToolbar(menuProvider: MenuProvider, title: String) {
supportActionBar?.setHomeAsUpIndicator(null)
addMenuProvider(menuProvider)
binding.content.toolbar.title = title
}
其他Fragment.kt
override fun onViewCreated() {
super.onViewCreated(view, savedInstanceState)
val menuProvider = object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.toolbar_nothing, menu) // empty menu layout
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return true
}
}
requireActivity().changeToolbar(menuProvider!!, "Other Fragment")
requireActivity().invalidateOptionsMenu()
}
此代码成功地将去雾标记更改为后退按钮,并将工具栏的标题固定为片段标题。 但是,还剩下两个菜单选项,即使按下返回按钮,openDrawer() 也会运行。 即使这种方式工作正常,我也必须为每个片段的 onViewCreated 运行活动的 changeToolbar()。
另一种方法不是管理活动中的工具栏,而是将每个片段的工具栏添加到 xml 中。但我认为这将是一个非常低效的操作。
我应该使用什么方法?
答:
我通过以下方式解决了它。
从不使用
setSupportActionBar()
在我的项目中,存在以下条件:
- 工具栏布局应在总共三个片段中共享:主片段、片段 A 和 B。BackButton 应显示在其余片段中。
- 在 Main Fragment 中,主页按钮必须打开导航视图(抽屉),而在 Fragment with Back Button 中,它必须移动到 Fragment 才能移动。().
popBackStack()
- 在主片段中,标题应为应用程序的徽标,在片段 A 和 B 中,应为片段的名称。
解决方案如下:
将每个工具栏布局添加到 MainActivity。(是否要创建文件并包含布局并不重要)
toolbar_layout.xml
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.MainActivity"> <androidx.appcompat.widget.Toolbar android:id="@+id/default_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_constraintTop_toTopOf="parent"> </androidx.appcompat.widget.Toolbar> <androidx.appcompat.widget.Toolbar android:id="@+id/backstack_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_constraintTop_toTopOf="parent" android:visibility="gone"> </androidx.appcompat.widget.Toolbar> <androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="0dp" app:navGraph="@navigation/nav_graph" app:defaultNavHost="true" android:layout_marginTop="?attr/actionBarSize" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toTopOf="parent"/> ... </androidx.constraintlayout.widget.ConstraintLayout>
调整在“活动”中移动片段所需的工具栏的可见性。在这个项目中,使用了 Jetpack Navigation,因此使用了 navController。addOnDestinationChangedListener
private fun initNavigationEvents() {
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.mainFragment,
R.id.fragmentA,
R.id.fragmentB,
)
)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host) as NavHostFragment
navController = navHostFragment.navController
navController.addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
R.id.mainFragment, R.id.fragmentA, R.id.fragmentB -> {
mViewDataBinding.defaultToolbar.visibility = View.VISIBLE
mViewDataBinding.backstackToolbar.visibility = View.GONE
}
else -> {
mViewDataBinding.defaultToolbar.visibility = View.GONE
mViewDataBinding.backstackToolbar.visibility = View.VISIBLE
}
}
}
...
}
- 这是导致活动一分为二的部分,正如我在答案顶部所写的,如果不将其用作 actionBar,则可以非常简单地解决它。
在 Activity 中添加 DrawerLayout。
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".ui.MainActivity">
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:openDrawer="start">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
// toolbars
// FragmentContainerView
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigation_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:layout_marginEnd="-64dp"
android:fitsSystemWindows="true"
app:headerLayout="@layout/navigation_left_drawer_header"
app:menu="@menu/bottom_nav_menu" />
</androidx.drawerlayout.widget.DrawerLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
如果工具栏设置为 ActionBar,则可以通过 来填充菜单,并通过 指定主页的操作。MenuProvider
android.R.id.home
onMenuItemSelected
但是,仅当仅使用一个工具栏时,才应使用此方法,并且如果要对每个片段以不同的方式使用工具栏,则不应将其指定为 ActionBar。因此,必须在不指定 ActionBar 的情况下在工具栏上膨胀菜单。
在工具栏中,可以将“主页”按钮的图像更改为 ,并且可以使用 设置单击事件。toolbar.setNavigationIcon()
toolbar.setNavigationOnClickListener
private fun initDefaultToolbar() {
mViewDataBinding.defaultToolbar.inflateMenu(R.menu.toolbar_activity)
mViewDataBinding.defaultToolbar.setNavigationIcon(R.drawable.ic_action_open_drawer)
mViewDataBinding.defaultToolbar.setOnMenuItemClickListener {
when (it.itemId) {
R.id.item1 -> {
Toast.makeText(this@MainActivity, "item1 clicked", Toast.LENGTH_SHORT).show()
return@setOnMenuItemClickListener true
}
R.id.item2 -> {
Toast.makeText(this@MainActivity, "item2 clicked", Toast.LENGTH_SHORT).show()
return@setOnMenuItemClickListener true
}
else -> {
return@setOnMenuItemClickListener false
}
}
}
mViewDataBinding.defaultToolbar.setNavigationOnClickListener {
mViewDataBinding.drawerLayout.openDrawer(GravityCompat.START)
}
}
private fun initBackstackToolbar() {
mViewDataBinding.backstackToolbar.setNavigationIcon(R.drawable.ic_action_back)
mViewDataBinding.backstackToolbar.setNavigationOnClickListener {
navController.popBackStack()
}
}
您可以通过添加 ImageView 来指示徽标,添加 TextView 来指示工具栏中的标题并根据需要调整可见性,从而轻松自定义它。
<androidx.appcompat.widget.Toolbar android:id="@+id/default_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_constraintTop_toTopOf="parent"> <ImageView android:id="@+id/app_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/ic_launcher_foreground" android:visibility="@{!vm.titleVisibility}" /> <TextView android:id="@+id/toolbar_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="@{vm.title}" android:visibility="@{vm.titleVisibility}" /> </androidx.appcompat.widget.Toolbar> <androidx.appcompat.widget.Toolbar android:id="@+id/backstack_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_constraintTop_toTopOf="parent" android:visibility="gone"> <TextView android:id="@+id/toolbar_title2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="@{vm.title}" android:visibility="@{vm.titleVisibility}" /> </androidx.appcompat.widget.Toolbar>
我通过将片段的标题从片段的 onViewCreated 传递给 Activity 并将数据传递给 ViewModel 并对其进行分支来实现它。
评论