如何使用导航架构组件在应用栏中设置标题
How to set title in app bar with Navigation Architecture Component
我正在试用导航架构组件,但现在在设置标题时遇到了困难。如何以编程方式设置标题及其工作原理?
为了澄清我的问题,让我们举个例子,我设置了一个简单的应用程序,其中 MainActivity
托管导航主机控制器,MainFragment
有一个按钮,点击按钮转到 DetailFragment
.
来自 stack-overflow 上 multiple app bars 的另一个问题的相同代码。
MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Setting up a back button
NavController navController = Navigation.findNavController(this, R.id.nav_host);
NavigationUI.setupActionBarWithNavController(this, navController);
}
@Override
public boolean onSupportNavigateUp() {
return Navigation.findNavController(this, R.id.nav_host).navigateUp();
}
}
MainFragment
public class MainFragment extends Fragment {
public MainFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_main, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button buttonOne = view.findViewById(R.id.button_one);
buttonOne.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.detailFragment));
}
}
DetailFragment
public class DetailFragment extends Fragment {
public DetailFragment() {
// Required empty public constructor
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_detail, container, false);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<fragment
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
android:layout_marginTop="?android:attr/actionBarSize"
app:defaultNavHost="true"
app:layout_anchor="@id/bottom_appbar"
app:layout_anchorGravity="top"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:navGraph="@navigation/mobile_navigation" />
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bottom_appbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:layout_gravity="bottom" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_anchor="@id/bottom_appbar" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
navigation.xml
<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.example.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main" >
<action
android:id="@+id/toAccountFragment"
app:destination="@id/detailFragment" />
</fragment>
<fragment
android:id="@+id/detailFragment"
android:name="com.example.DetailFragment"
android:label="fragment_account"
tools:layout="@layout/fragment_detail" />
</navigation>
所以当启动我的应用程序时,标题是 "MainActivity"。与往常一样,它显示 MainFragment
,其中包含转到 DetailFragment
的按钮。在 DialogFragment
中,我将标题设置为:
getActivity().getSupportActionBar().setTitle("Detail");
第一个问题: 所以点击 MainFragment
上的按钮转到 DetailFragment
,它确实转到了那里,标题变为 "Detail".但是在单击后退按钮时, 标题会更改为 "fragment_main"。所以我将这行代码添加到 MainFragment
:
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// ...
//Showing the title
Navigation.findNavController(view)
.getCurrentDestination().setLabel("Hello");
}
现在从 DetailFragment
返回到 MainFragment
时,标题更改为 "Hello"。但是 第二个问题 来了,当我关闭应用程序并重新启动时,标题变回 "MainActivity" 虽然它应该显示 "Hello" 知道吗?
好的,然后在 MainFrgment
中添加 setTitle("Hello")
也不行。例如activity开始,标题是"Hello",转到DetailsFragment
再按返回键,标题又回到"fragment_main"。
唯一的解决办法是在 MainFragment
.
中同时使用 setTitle("Hello")
和 Navigation.findNavController(view).getCurrentDestination().setLabel("Hello")
那么使用导航组件显示片段标题的正确方法是什么?
其实是因为:
android:label="fragment_main"
你在xml中设置的。
So what is the proper way to show the title for Fragment
s using
Navigation Component?
setTitle()
此时有效。但是,因为您为那些 Fragment
设置了标签,它可能会在重新创建 Activity
时再次显示标签。解决方案可能是删除 android:label
然后用代码做你的事情:
((AppCompatActivity) getActivity()).getSupportActionBar().setTitle("your title");
或者:
((AppCompatActivity) getActivity()).getSupportActionBar().setSubtitle("your subtitle");
在onCreateView()
.
找到解决方法:
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")
也看看这个:
我建议在每个屏幕中包含 AppBar。
为避免代码重复,创建一个构建 AppBar 的助手,将标题作为参数。然后在每个屏幕中调用助手 class.
由于其他人仍在参与回答这个问题,让我回答我自己的问题,因为APIs 从那时起已经发生了变化。
首先,从 navigation.xml
(又名导航图)中删除您希望更改其标题的 fragment/s 中的 android:label
。
现在您可以通过调用
更改 Fragment
的标题
(requireActivity() as MainActivity).title = "My title"
但是您应该使用的首选方式是 MainActivity
中的 API NavController.addOnDestinationChangedListener
。一个例子:
NavController.OnDestinationChangedListener { controller, destination, arguments ->
// compare destination id
title = when (destination.id) {
R.id.someFragment -> "My title"
else -> "Default title"
}
// if (destination == R.id.someFragment) {
// title = "My title"
// } else {
// title = "Default Title"
// }
}
根据经验,NavController.addOnDestinationChangedListener
似乎表现不错。下面我在 MainActivity 上的示例发挥了神奇作用
navController.addOnDestinationChangedListener{ controller, destination, arguments ->
title = when (destination.id) {
R.id.navigation_home -> "My title"
R.id.navigation_task_start -> "My title2"
R.id.navigation_task_finish -> "My title3"
R.id.navigation_status -> "My title3"
R.id.navigation_settings -> "My title4"
else -> "Default title"
}
}
如今使用 Kotlin 和 androidx.navigation:navigation-ui-ktx
:
可以更轻松地实现这一目标
import androidx.navigation.ui.setupActionBarWithNavController
class MainActivity : AppCompatActivity() {
private val navController: NavController
get() = findNavController(R.id.nav_host_fragment)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main)
setSupportActionBar(binding.toolbar)
setupActionBarWithNavController(navController) // <- the most important line
}
// Required by the docs for setupActionBarWithNavController(...)
override fun onSupportNavigateUp() = navController.navigateUp()
}
基本上就是这样。不要忘记在导航图中指定 android:label
。
另一种方法可能是这样的:
fun Fragment.setToolbarTitle(title: String) {
(activity as NavigationDrawerActivity).supportActionBar?.title = title
}
API 可能在提出问题后发生了变化,但现在您可以在导航图标签中指示对应用资源中字符串的引用。
如果您想要片段的静态标题,这将非常有用。
如果您不指定您的应用栏(默认应用栏),您可以在您的片段中使用此代码
(activity as MainActivity).supportActionBar?.title = "Your Custom Title"
记得删除导航图中的 android:label
属性
开心码^-^
您可以使用导航图 xml 文件并将片段的标签设置为参数。
然后,在您的 parent 片段中,您可以使用 SafeArgs 传递参数(请按照 https://developer.android.com/guide/navigation/navigation-pass-data#Safe-args 上的指南设置 SafeArgs)并提供默认值以避免标题为 null 或空。
<!--this is originating fragment-->
<fragment
android:id="@+id/fragmentA"
android:name=".ui.FragmentA"
tools:layout="@layout/fragment_a">
<action
android:id="@+id/fragmentBAction"
app:destination="@id/fragmentB" />
</fragment>
<!--set the fragment's title to a string passed as an argument-->
<!--this is a destination fragment (assuming you're navigating FragmentA to FragmentB)-->
<fragment
android:id="@+id/fragmentB"
android:name="ui.FragmentB"
android:label="{myTitle}"
tools:layout="@layout/fragment_b">
<argument
android:name="myTitle"
android:defaultValue="defaultTitle"
app:argType="string" />
</fragment>
在原始片段中:
public void onClick(View view) {
String text = "text to pass as a title";
FragmentADirections.FragmentBAction action = FragmentADirections.fragmentBAction();
action.setMyTitle(text);
Navigation.findNavController(view).navigate(action);
}
FragmentADirections 和 FragmentBAction -
这些 class 是根据您 nav_graph.xml 文件中的 ID 自动生成的。
在 'your action ID + Action' 中键入 classes 你可以找到 auto-generated setter 方法,你可以用它来传递参数
在你的接收目的地,你调用auto-generated class {你的接收片段ID}+Args
FragmentBArgs.fromBundle(requireArguments()).getMyTitle();
请参考官方指南
https://developer.android.com/guide/navigation/navigation-pass-data#Safe-args
NavigationUI.setupActionBarWithNavController(this, navController)
Don't forget to specify android:label for your fragments in your nav graphs.
向后导航:
override fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(navController, null)
}
删除导航图 xml 文件中的详细片段标签。
然后通过参数将首选标题传递给需要标题的目标片段。
第一个片段代码-起点
findNavController().navigate(
R.id.action_FirstFragment_to_descriptionFragment,
bundleOf(Pair("toolbar_title", "My Details Fragment Title"))
)
如您所见,我在导航到目标片段时在参数包中成对发送
所以在你的目标片段中从 onCreate 方法中的参数获取标题,就像这样
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
toolbarTitle = requireArguments().getString("toolbar_title", "")
}
然后使用它在 onCreateView 方法中像这样更改 Main Activity 的标题
requireActivity().toolbar.title = toolbarTitle
使用导航中的任一标签更新标题 xml 或排除标签并使用 requiresActivity().title
设置 支持混合使用带有和不带有动态标题的屏幕的两种方式。使用 Compose UI 工具栏和选项卡为我工作。
val titleLiveData = MutableLiveData<String>()
findNavController().addOnDestinationChangedListener { _, destination, _ ->
destination.label?.let {
titleLiveData.value = destination.label.toString()
}
}
(requireActivity() as AppCompatActivity).setSupportActionBar(object: Toolbar(requireContext()) {
override fun setTitle(title: CharSequence?) {
titleLiveData.value = title.toString()
}
})
titleLiveData.observe(viewLifecycleOwner, {
// Update your title
})
无论如何,我尝试过的那些答案中有几个没有用,所以我决定在 Kotlin 中使用接口
以旧的 Java 方式来做
创建界面如下图。
interface OnTitleChangeListener {
fun onTitleChange(app_title: String)
}
然后让我的activity实现这个接口如下图
class HomeActivity : AppCompatActivity(), OnTitleChangeListener {
override fun onTitleChange(app_title: String) {
title = app_title
}
}
我的片段在 activity 上如何,我这个
override fun onAttach(context: Context) {
super.onAttach(context)
this.currentContext = context
(context as HomeActivity).onTitleChange("New Title For The app")
}
我花了几个小时尝试做一件非常简单的事情,例如在从主片段上的特定项目导航时更改详细片段的栏标题。我爸爸总是对我说:K.I.S.S。保持简单,儿子。
setTitle() 崩溃了,接口很繁琐,在代码行方面提出了一个(非常)简单的解决方案,用于最新的片段导航实现。
在主片段中解析 NavController,获取 NavGraph,找到目标节点,设置标题,最后但并非最不重要的导航到它:
//----------------------------------------------------------------------------------------------
private void navigateToDetail() {
NavController navController = NavHostFragment.findNavController(FragmentMaster.this);
navController.getGraph().findNode(R.id.FragmentDetail).setLabel("Master record detail");
navController.navigate(R.id.action_FragmentMaster_to_FragmentDetail,null);
}
简单的解决方法:
布局
androidx.coordinatorlayout.widget.CoordinatorLayout
com.google.android.material.appbar.AppBarLayout
com.google.android.material.appbar.MaterialToolbar
androidx.constraintlayout.widget.ConstraintLayout
com.google.android.material.bottomnavigation.BottomNavigationView
fragment
androidx.navigation.fragment.NavHostFragment
Activity
binding = DataBindingUtil.setContentView(this, layoutRes)
setSupportActionBar(binding.toolbar)
val controller = findNavController(R.id.nav_host)
val configuration = AppBarConfiguration(
setOf(
R.id.navigation_home,
R.id.navigation_dashboard,
R.id.navigation_notifications
)
)
setupActionBarWithNavController(controller, configuration)
navView.setupWithNavController(controller)
如果您想使用 Activity 和 Fragment 之间的低内聚代码以编程方式更改工具栏的标题,这可能会有所帮助。
class DetailsFragment : Fragment() {
interface Callbacks {
fun updateTitle(title: String)
}
private var listener: Callbacks? = null
override fun onAttach(context: Context) {
super.onAttach(context)
// keep activity as interface only
if (context is Callbacks) {
listener = context
}
}
override fun onDetach() {
// forget about activity
listener = null
super.onDetach()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View =
inflater.inflate(R.layout.fragment_details, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
listener?.updateTitle("Dynamic generated title")
}
}
class MainActivity : AppCompatActivity(), DetailsFragment.Callbacks {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun updateTitle(title: String) {
supportActionBar?.title = title
}
}
2021 年 9 月 4 日
在我的例子中使用 Kotlin 这是解决方案。在 MainActivity
中使用此代码
val navController = this.findNavController(R.id.myNavHostFragment)
navController.addOnDestinationChangedListener { controller, destination, arguments ->
destination.label = when (destination.id) {
R.id.homeFragment -> resources.getString(R.string.app_name)
R.id.roomFloorTilesFragment -> resources.getString(R.string.room_floor_tiles)
R.id.roomWallTilesFragment -> resources.getString(R.string.room_wall_tiles)
R.id.ceilingRateFragment -> resources.getString(R.string.ceiling_rate)
R.id.areaConverterFragment -> resources.getString(R.string.area_converter)
R.id.unitConverterFragment -> resources.getString(R.string.unit_converter)
R.id.lenterFragment -> resources.getString(R.string.lenter_rate)
R.id.plotSizeFragment -> resources.getString(R.string.plot_size)
else -> resources.getString(R.string.app_name)
}
}
NavigationUI.setupActionBarWithNavController(...)
NavigationUI.setupWithNavController(...)
我正在试用导航架构组件,但现在在设置标题时遇到了困难。如何以编程方式设置标题及其工作原理?
为了澄清我的问题,让我们举个例子,我设置了一个简单的应用程序,其中 MainActivity
托管导航主机控制器,MainFragment
有一个按钮,点击按钮转到 DetailFragment
.
来自 stack-overflow 上 multiple app bars 的另一个问题的相同代码。
MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Setting up a back button
NavController navController = Navigation.findNavController(this, R.id.nav_host);
NavigationUI.setupActionBarWithNavController(this, navController);
}
@Override
public boolean onSupportNavigateUp() {
return Navigation.findNavController(this, R.id.nav_host).navigateUp();
}
}
MainFragment
public class MainFragment extends Fragment {
public MainFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_main, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button buttonOne = view.findViewById(R.id.button_one);
buttonOne.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.detailFragment));
}
}
DetailFragment
public class DetailFragment extends Fragment {
public DetailFragment() {
// Required empty public constructor
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_detail, container, false);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<fragment
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
android:layout_marginTop="?android:attr/actionBarSize"
app:defaultNavHost="true"
app:layout_anchor="@id/bottom_appbar"
app:layout_anchorGravity="top"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:navGraph="@navigation/mobile_navigation" />
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bottom_appbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:layout_gravity="bottom" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_anchor="@id/bottom_appbar" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
navigation.xml
<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.example.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main" >
<action
android:id="@+id/toAccountFragment"
app:destination="@id/detailFragment" />
</fragment>
<fragment
android:id="@+id/detailFragment"
android:name="com.example.DetailFragment"
android:label="fragment_account"
tools:layout="@layout/fragment_detail" />
</navigation>
所以当启动我的应用程序时,标题是 "MainActivity"。与往常一样,它显示 MainFragment
,其中包含转到 DetailFragment
的按钮。在 DialogFragment
中,我将标题设置为:
getActivity().getSupportActionBar().setTitle("Detail");
第一个问题: 所以点击 MainFragment
上的按钮转到 DetailFragment
,它确实转到了那里,标题变为 "Detail".但是在单击后退按钮时, 标题会更改为 "fragment_main"。所以我将这行代码添加到 MainFragment
:
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// ...
//Showing the title
Navigation.findNavController(view)
.getCurrentDestination().setLabel("Hello");
}
现在从 DetailFragment
返回到 MainFragment
时,标题更改为 "Hello"。但是 第二个问题 来了,当我关闭应用程序并重新启动时,标题变回 "MainActivity" 虽然它应该显示 "Hello" 知道吗?
好的,然后在 MainFrgment
中添加 setTitle("Hello")
也不行。例如activity开始,标题是"Hello",转到DetailsFragment
再按返回键,标题又回到"fragment_main"。
唯一的解决办法是在 MainFragment
.
setTitle("Hello")
和 Navigation.findNavController(view).getCurrentDestination().setLabel("Hello")
那么使用导航组件显示片段标题的正确方法是什么?
其实是因为:
android:label="fragment_main"
你在xml中设置的。
So what is the proper way to show the title for
Fragment
s using Navigation Component?
setTitle()
此时有效。但是,因为您为那些 Fragment
设置了标签,它可能会在重新创建 Activity
时再次显示标签。解决方案可能是删除 android:label
然后用代码做你的事情:
((AppCompatActivity) getActivity()).getSupportActionBar().setTitle("your title");
或者:
((AppCompatActivity) getActivity()).getSupportActionBar().setSubtitle("your subtitle");
在onCreateView()
.
找到解决方法:
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")
也看看这个:
我建议在每个屏幕中包含 AppBar。 为避免代码重复,创建一个构建 AppBar 的助手,将标题作为参数。然后在每个屏幕中调用助手 class.
由于其他人仍在参与回答这个问题,让我回答我自己的问题,因为APIs 从那时起已经发生了变化。
首先,从 navigation.xml
(又名导航图)中删除您希望更改其标题的 fragment/s 中的 android:label
。
现在您可以通过调用
更改Fragment
的标题
(requireActivity() as MainActivity).title = "My title"
但是您应该使用的首选方式是 MainActivity
中的 API NavController.addOnDestinationChangedListener
。一个例子:
NavController.OnDestinationChangedListener { controller, destination, arguments ->
// compare destination id
title = when (destination.id) {
R.id.someFragment -> "My title"
else -> "Default title"
}
// if (destination == R.id.someFragment) {
// title = "My title"
// } else {
// title = "Default Title"
// }
}
根据经验,NavController.addOnDestinationChangedListener
似乎表现不错。下面我在 MainActivity 上的示例发挥了神奇作用
navController.addOnDestinationChangedListener{ controller, destination, arguments ->
title = when (destination.id) {
R.id.navigation_home -> "My title"
R.id.navigation_task_start -> "My title2"
R.id.navigation_task_finish -> "My title3"
R.id.navigation_status -> "My title3"
R.id.navigation_settings -> "My title4"
else -> "Default title"
}
}
如今使用 Kotlin 和 androidx.navigation:navigation-ui-ktx
:
import androidx.navigation.ui.setupActionBarWithNavController
class MainActivity : AppCompatActivity() {
private val navController: NavController
get() = findNavController(R.id.nav_host_fragment)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main)
setSupportActionBar(binding.toolbar)
setupActionBarWithNavController(navController) // <- the most important line
}
// Required by the docs for setupActionBarWithNavController(...)
override fun onSupportNavigateUp() = navController.navigateUp()
}
基本上就是这样。不要忘记在导航图中指定 android:label
。
另一种方法可能是这样的:
fun Fragment.setToolbarTitle(title: String) {
(activity as NavigationDrawerActivity).supportActionBar?.title = title
}
API 可能在提出问题后发生了变化,但现在您可以在导航图标签中指示对应用资源中字符串的引用。
如果您想要片段的静态标题,这将非常有用。
如果您不指定您的应用栏(默认应用栏),您可以在您的片段中使用此代码
(activity as MainActivity).supportActionBar?.title = "Your Custom Title"
记得删除导航图中的 android:label
属性
开心码^-^
您可以使用导航图 xml 文件并将片段的标签设置为参数。 然后,在您的 parent 片段中,您可以使用 SafeArgs 传递参数(请按照 https://developer.android.com/guide/navigation/navigation-pass-data#Safe-args 上的指南设置 SafeArgs)并提供默认值以避免标题为 null 或空。
<!--this is originating fragment-->
<fragment
android:id="@+id/fragmentA"
android:name=".ui.FragmentA"
tools:layout="@layout/fragment_a">
<action
android:id="@+id/fragmentBAction"
app:destination="@id/fragmentB" />
</fragment>
<!--set the fragment's title to a string passed as an argument-->
<!--this is a destination fragment (assuming you're navigating FragmentA to FragmentB)-->
<fragment
android:id="@+id/fragmentB"
android:name="ui.FragmentB"
android:label="{myTitle}"
tools:layout="@layout/fragment_b">
<argument
android:name="myTitle"
android:defaultValue="defaultTitle"
app:argType="string" />
</fragment>
在原始片段中:
public void onClick(View view) {
String text = "text to pass as a title";
FragmentADirections.FragmentBAction action = FragmentADirections.fragmentBAction();
action.setMyTitle(text);
Navigation.findNavController(view).navigate(action);
}
FragmentADirections 和 FragmentBAction - 这些 class 是根据您 nav_graph.xml 文件中的 ID 自动生成的。 在 'your action ID + Action' 中键入 classes 你可以找到 auto-generated setter 方法,你可以用它来传递参数
在你的接收目的地,你调用auto-generated class {你的接收片段ID}+Args
FragmentBArgs.fromBundle(requireArguments()).getMyTitle();
请参考官方指南 https://developer.android.com/guide/navigation/navigation-pass-data#Safe-args
NavigationUI.setupActionBarWithNavController(this, navController)
Don't forget to specify android:label for your fragments in your nav graphs.
向后导航:
override fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(navController, null)
}
删除导航图 xml 文件中的详细片段标签。
然后通过参数将首选标题传递给需要标题的目标片段。
第一个片段代码-起点
findNavController().navigate(
R.id.action_FirstFragment_to_descriptionFragment,
bundleOf(Pair("toolbar_title", "My Details Fragment Title"))
)
如您所见,我在导航到目标片段时在参数包中成对发送
所以在你的目标片段中从 onCreate 方法中的参数获取标题,就像这样
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
toolbarTitle = requireArguments().getString("toolbar_title", "")
}
然后使用它在 onCreateView 方法中像这样更改 Main Activity 的标题
requireActivity().toolbar.title = toolbarTitle
使用导航中的任一标签更新标题 xml 或排除标签并使用 requiresActivity().title
设置 支持混合使用带有和不带有动态标题的屏幕的两种方式。使用 Compose UI 工具栏和选项卡为我工作。
val titleLiveData = MutableLiveData<String>()
findNavController().addOnDestinationChangedListener { _, destination, _ ->
destination.label?.let {
titleLiveData.value = destination.label.toString()
}
}
(requireActivity() as AppCompatActivity).setSupportActionBar(object: Toolbar(requireContext()) {
override fun setTitle(title: CharSequence?) {
titleLiveData.value = title.toString()
}
})
titleLiveData.observe(viewLifecycleOwner, {
// Update your title
})
无论如何,我尝试过的那些答案中有几个没有用,所以我决定在 Kotlin 中使用接口
以旧的 Java 方式来做创建界面如下图。
interface OnTitleChangeListener {
fun onTitleChange(app_title: String)
}
然后让我的activity实现这个接口如下图
class HomeActivity : AppCompatActivity(), OnTitleChangeListener {
override fun onTitleChange(app_title: String) {
title = app_title
}
}
我的片段在 activity 上如何,我这个
override fun onAttach(context: Context) {
super.onAttach(context)
this.currentContext = context
(context as HomeActivity).onTitleChange("New Title For The app")
}
我花了几个小时尝试做一件非常简单的事情,例如在从主片段上的特定项目导航时更改详细片段的栏标题。我爸爸总是对我说:K.I.S.S。保持简单,儿子。 setTitle() 崩溃了,接口很繁琐,在代码行方面提出了一个(非常)简单的解决方案,用于最新的片段导航实现。 在主片段中解析 NavController,获取 NavGraph,找到目标节点,设置标题,最后但并非最不重要的导航到它:
//----------------------------------------------------------------------------------------------
private void navigateToDetail() {
NavController navController = NavHostFragment.findNavController(FragmentMaster.this);
navController.getGraph().findNode(R.id.FragmentDetail).setLabel("Master record detail");
navController.navigate(R.id.action_FragmentMaster_to_FragmentDetail,null);
}
简单的解决方法:
布局
androidx.coordinatorlayout.widget.CoordinatorLayout
com.google.android.material.appbar.AppBarLayout
com.google.android.material.appbar.MaterialToolbar
androidx.constraintlayout.widget.ConstraintLayout
com.google.android.material.bottomnavigation.BottomNavigationView
fragment
androidx.navigation.fragment.NavHostFragment
Activity
binding = DataBindingUtil.setContentView(this, layoutRes)
setSupportActionBar(binding.toolbar)
val controller = findNavController(R.id.nav_host)
val configuration = AppBarConfiguration(
setOf(
R.id.navigation_home,
R.id.navigation_dashboard,
R.id.navigation_notifications
)
)
setupActionBarWithNavController(controller, configuration)
navView.setupWithNavController(controller)
如果您想使用 Activity 和 Fragment 之间的低内聚代码以编程方式更改工具栏的标题,这可能会有所帮助。
class DetailsFragment : Fragment() {
interface Callbacks {
fun updateTitle(title: String)
}
private var listener: Callbacks? = null
override fun onAttach(context: Context) {
super.onAttach(context)
// keep activity as interface only
if (context is Callbacks) {
listener = context
}
}
override fun onDetach() {
// forget about activity
listener = null
super.onDetach()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View =
inflater.inflate(R.layout.fragment_details, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
listener?.updateTitle("Dynamic generated title")
}
}
class MainActivity : AppCompatActivity(), DetailsFragment.Callbacks {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun updateTitle(title: String) {
supportActionBar?.title = title
}
}
2021 年 9 月 4 日
在我的例子中使用 Kotlin 这是解决方案。在 MainActivity
中使用此代码 val navController = this.findNavController(R.id.myNavHostFragment)
navController.addOnDestinationChangedListener { controller, destination, arguments ->
destination.label = when (destination.id) {
R.id.homeFragment -> resources.getString(R.string.app_name)
R.id.roomFloorTilesFragment -> resources.getString(R.string.room_floor_tiles)
R.id.roomWallTilesFragment -> resources.getString(R.string.room_wall_tiles)
R.id.ceilingRateFragment -> resources.getString(R.string.ceiling_rate)
R.id.areaConverterFragment -> resources.getString(R.string.area_converter)
R.id.unitConverterFragment -> resources.getString(R.string.unit_converter)
R.id.lenterFragment -> resources.getString(R.string.lenter_rate)
R.id.plotSizeFragment -> resources.getString(R.string.plot_size)
else -> resources.getString(R.string.app_name)
}
}
NavigationUI.setupActionBarWithNavController(...)
NavigationUI.setupWithNavController(...)