使用 AndroidX 导航的片段中的动态 ActionBar 标题

Dynamic ActionBar title from a Fragment using AndroidX Navigation

我正在使用来自 Android Jetpack 的新 Navigation 组件。

root Activity 设置非常简单:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setSupportActionBar(toolbar)

    val navController = findNavController(R.id.navigationFragment)
    setupActionBarWithNavController(navController)

    bottomNavigationView.setupWithNavController(navController)
}

在导航图中定义片段的标题时效果很好。但是对于一个片段,我想动态设置标题。

我试过 findNavController().currentDestination.label = "Hello world" 但它什么也没做。

我当然可以使用像 (activity as? AppCompatActivity)?.supportActionBar?.title = "Hello world" 这样的技巧,但我觉得它会破坏 setupActionBarWithNavController() 为我所做的魔法。有什么方法可以动态更新 Action Bar 标题吗?

您可以在 activity 中添加 addOnNavigatedListener,并根据当前目的地更改标题

 findNavController(nav_host_fragment).addOnNavigatedListener { controller, destination ->
        when(destination.id) {
            R.id.destination1 -> {
                my_toolbar.title= "Some title"
            }
            R.id.destination2 -> {
                my_toolbar.title= "Othertitle"

            }

    }
}

另一种解决方案是使用 ViewModel 和 LiveData,将 viewmodel 附加到您的 activity 和片段,在 viewmodel

中添加一个 livedata 字段
val title = MutableLiveData<String>()

从你的 activity 观察这个字段,如果它被改变更新工具栏标题

viewModel?.title?.observe(this, Observer { 
        my_toolbar.title=it
    })

从您想要的片段更改视图模型中的标题字段

viewModel?.title?.value="New title"

截至目前,Jetpack 导航架构组件不提供任何 "built in" 方法来执行此操作,您必须实现自己的 "custom" 方法来执行此操作。

现有的功能请求是获取添加到新 Jetpack 导航架构组件的目的地上的动态标签功能。如果您是因为 want/need 此功能而来到这里,请在此处为现有功能请求加注星标: https://issuetracker.google.com/issues/80267266

issue 修复之前,简单的监听器正在为我工​​作:

/**
 * Temporary solution to dynamically change title of actionbar controlled by Navigation component
 * Should be removed as soon as the bug on Navigation will be fixed: (https://issuetracker.google.com/issues/80267266)
 */
interface TempToolbarTitleListener {
    fun updateTitle(title: String)
}

class MainActivity : AppCompatActivity(), TempToolbarTitleListener {

    ...

    override fun updateTitle(title: String) {
        binding.toolbar.title = title
    }
}

更改片段的标题:

(activity as TempToolbarTitleListener).updateTitle("custom title")

考虑到您的主机 activity 是 MainActivity,只需将以下代码添加到您的 MainActivityonCreate 乐趣

val navController = Navigation.findNavController(this, R.id.nav_host_fragment)

// setting title according to fragment
navController.addOnDestinationChangedListener { 
    controller, destination, arguments ->
        toolbar.title = navController.currentDestination?.label
}

从 graph.xml 文件中删除标签

android:label="fragment_info"

如果您想从片段本身动态设置片段的标题,请使用老式方法

getActivity().setTitle("Your Title");

在尝试 activity 的标题时,它似乎覆盖了片段的标题。为了安全起见,你必须穿上 onResume.

override fun onResume() {
    super.onResume()
    activity?.toolbar.title = "YOUR_TITLE_HERE"
}

对我有用!

注意: activity

中必须有工具栏小部件

在您的 activity 的 xml

中添加这样的工具栏
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </com.google.android.material.appbar.AppBarLayout>

    <!-- Other Widgets -->

</androidx.coordinatorlayout.widget.CoordinatorLayout>

1.0.0-alpha08 开始,您可以让 NavigationUI 位动态设置标题...如果动态位是导航操作的参数。

因此,例如,在您的导航图中,您可以有这样的内容:

  <fragment
    android:id="@+id/displayFragment"
    android:name="com.commonsware.jetpack.sampler.nav.DisplayFragment"
    android:label="Title: {title}" >
    <argument
      android:name="modelId"
      app:argType="string" />
    <argument
      android:name="title"
      app:argType="string" />
  </fragment>

在这里,我们 <fragment>android:label 属性有一个用大括号括起来的参数名称({title} in "Title: {title}")。应用栏的标题将被设置为标签的值,{title} 替换为 title 参数的值。

如果您需要比这更复杂的东西——例如,您想通过 ID 查找模型并从中读取 属性——您将需要使用更多手动方法,例如概述的方法在这个问题的其他答案中。

好吧,现在导航 UI 支持此功能。现在 ActionBar 标题会动态变化。您只需将 ActionBar 设置为 NavController.

private lateinit var appBarConfiguration: AppBarConfiguration

private lateinit var navController: NavController

override fun onCreate(savedInstanceState: Bundle?) {
    preferedTheme()
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setSupportActionBar(toolbar)
    navController = findNavController(R.id.nav_controller_fragment)
    appBarConfiguration = AppBarConfiguration(navController.graph)
    setupActionBarWithNavController(navController, appBarConfiguration)
}

并在导航图中设置操作栏标签:

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/mobile_navigation"
        app:startDestination="@id/mainFragment">

<fragment android:id="@+id/mainFragment"
          android:name="com.cinderellaman.general.ui.fragments.MainFragment"
          android:label="General"
          tools:layout="@layout/main_fragment"/>

现在还支持向上导航:

override fun onSupportNavigateUp(): Boolean {
    return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}

你可以去掉导航图中的android:label然后在onCreateView()

中写入
activity?.title="your title"

如果您在 Activity 中使用 setSupportActionBar 上的工具栏,并且您想在片段中更改其标题,那么下面的代码可能会对您有所帮助 ;)

(requireActivity() as MainActivity).toolbar.title = "Title here"

可以通过将 activity 转换为 AppCompatActivity 来更改片段中的标题。

Kotlin

(requireActivity() as AppCompatActivity).supportActionBar?.title = "Hello"

Java

((AppCompatActivity) requireActivity()).getSupportActionBar().setTitle("Hello");

如果您的标题是从串联字符串中收到的,例如:

ActionBar actionBar = ((AppCompatActivity) 
  requireActivity()).getSupportActionBar();
  
if(actionBar!=null)                   
   actionBar.setTitle(count + "items"); 

根据以下解决方案,无需手动访问工具栏

如果您要将模型传递到目标片段中。 然后你就可以按照下面的方式去做了。

覆盖模型中的 toString() 方法 class

data class UserModel(
    val userId: String = ""
    val firstName: String = "",
    val lastName: String = ""  
) {

    override fun toString(): String {
        return "$firstName $lastName"
    }
}

现在,在 nav_grap.xml 文件中,执行以下操作。 android:label="{UserData}" 将从模型 class.

的 toString() 方法中获取标签
<fragment
    android:id="@+id/messagesFragment"
    android:name="com.app.mydemoapp.ui.messages.MessagesFragment"
    android:label="{UserData}"
    tools:layout="@layout/fragment_messages">

    <argument
        android:name="UserData"
        app:argType="com.app.mydemoapp.model.UserModel" />

</fragment>

希望对您有所帮助。

基于@kaustubh-trivedi 的回答,如果您使用的是 MVVM(如 Android Studio FirstFragment/SecondFragment 示例):

KOTLIN 版本

val navController = Navigation.findNavController(this, R.id.nav_host_fragment)

// setting title according to fragment
navController.addOnDestinationChangedListener { 
    controller, destination, arguments ->
        toolbar.title = navController.currentDestination?.label
}

JAVA版本

        navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
            @Override
            public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
                binding.toolbar.setTitle(destination.getLabel());
            }
        });