如何为每个片段应用不同的工具栏(actionbar)布局?

How to apply different toolbar(actionbar) layout for each fragment?

提问人:Haeiny 提问时间:9/4/2023 更新时间:9/8/2023 访问量:62

问:

我正在应用单活动架构,我想使用工具栏使用顶部固定栏。

在我的项目中,工具栏的形式因片段而异,形式如下:

主(主页)

几个(1 或 2)个带抽屉的碎片

其他片段

在 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 中。但我认为这将是一个非常低效的操作。

我应该使用什么方法?

Kotlin Android-fragments Android-Activity Android- 工具栏

评论

0赞 ianhanniballake 9/4/2023
如果每个片段具有不同的工具栏,则在每个片段中使用工具栏是文档中的指南
0赞 Haeiny 9/7/2023
@ianhanniballake 在我的项目中,许多片段(至少 10 个或更多)都是同一工具栏的形式,除了两个,只有标题发生了变化。在这种情况下,我是否仍应在每个片段上使用工具栏 api?

答:

0赞 Haeiny 9/8/2023 #1

我通过以下方式解决了它。

从不使用setSupportActionBar()

在我的项目中,存在以下条件:

  1. 工具栏布局应在总共三个片段中共享:主片段、片段 A 和 B。BackButton 应显示在其余片段中。
  2. 在 Main Fragment 中,主页按钮必须打开导航视图(抽屉),而在 Fragment with Back Button 中,它必须移动到 Fragment 才能移动。().popBackStack()
  3. 在主片段中,标题应为应用程序的徽标,在片段 A 和 B 中,应为片段的名称。

解决方案如下:

  1. 将每个工具栏布局添加到 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
                }
            }
        }
        ...
    }

  1. 这是导致活动一分为二的部分,正如我在答案顶部所写的,如果不将其用作 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,则可以通过 来填充菜单,并通过 指定主页的操作。MenuProviderandroid.R.id.homeonMenuItemSelected

但是,仅当仅使用一个工具栏时,才应使用此方法,并且如果要对每个片段以不同的方式使用工具栏,则不应将其指定为 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()
    }
}

  1. 您可以通过添加 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 并对其进行分支来实现它。