J'essayais le composant d'architecture de navigation et j'ai maintenant des difficultés à définir le titre. Comment définir le titre par programme et comment cela fonctionne-t-il?
Pour effacer ma question, prenons un exemple, où, j'ai installé une application simple avec MainActivity
hébergeant le contrôleur hôte de navigation, le MainFragment
a un bouton et en cliquant sur le bouton il va à DetailFragment
.
Le même code d'une autre question de plusieurs barres d'application sur le débordement de pile.
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>
Ainsi, lorsque je démarre mon application, le titre est "MainActivity" . Comme d'habitude, il montre le MainFragment
qui contient le bouton pour aller à DetailFragment
. Dans le DialogFragment
j'ai défini le titre comme:
getActivity().getSupportActionBar().setTitle("Detail");
Premier problème: Donc, en cliquant sur le bouton sur le MainFragment
pour aller à DetailFragment
, il y va et le titre change détailler". Mais en cliquant sur le bouton de retour, le titre devient "fragment_main" . J'ai donc ajouté cette ligne de code à MainFragment
:
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// ...
//Showing the title
Navigation.findNavController(view)
.getCurrentDestination().setLabel("Hello");
}
Maintenant, tout en revenant de DetailFragment
à MainFragment
, le titre devient "Bonjour". Mais voici le deuxième problème , lorsque je ferme l'application et que je recommence, le titre revient à "MainActivity" bien qu'il devrait afficher "Bonjour" à la place, tu sais?
Ok, puis ajouter setTitle("Hello")
dans MainFrgment
ne fonctionne pas trop. Par exemple, l'activité démarre et le titre est "Bonjour", allez dans DetailsFragment
et appuyez à nouveau sur le bouton Retour, le titre revient à "fragment_main".
La seule solution est d'avoir à la fois setTitle("Hello")
et Navigation.findNavController(view).getCurrentDestination().setLabel("Hello")
dans MainFragment
.
Alors, quelle est la bonne façon d'afficher le titre des fragments à l'aide du composant de navigation?
C'est en fait à cause de:
Android:label="fragment_main"
Que vous avez défini dans le xml.
Alors, quelle est la bonne façon d'afficher le titre pour
Fragment
s en utilisant le composant de navigation?
setTitle()
fonctionne à ce stade. Mais, comme vous définissez le libellé pour ces Fragment
, il peut afficher à nouveau le libellé lors de la recréation du Activity
. La solution supprimera probablement Android:label
Et fera ensuite vos choses avec du code:
getActivity().getSupportActionBar().setTitle("yourtitle");
Ou:
((AppCompatActivity) getActivity()).getSupportActionBar().setSubtitle();
Dans onCreateView()
.
Solution de contournement trouvée:
interface TempToolbarTitleListener {
fun updateTitle(title: String)
}
class MainActivity : AppCompatActivity(), TempToolbarTitleListener {
...
override fun updateTitle(title: String) {
binding.toolbar.title = title
}
}
Ensuite:
(activity as TempToolbarTitleListener).updateTitle("custom title")
Vérifiez également ceci: titre Dynamic ActionBar d'un fragment utilisant la navigation AndroidX
Vous pouvez utiliser ce code dans votre fragment si vous ne spécifiez pas votre barre d'application (barre d'application par défaut)
(activity as MainActivity).supportActionBar?.title = "Your Custom Title"
N'oubliez pas de supprimer le Android:label
attribut dans votre graphique de navigation
Code heureux ^ - ^
Je suggère d'inclure AppBar dans chaque écran. Pour éviter les doublons de code, créez un assistant, qui construit AppBar, en prenant le titre comme paramètre. Appelez ensuite l'assistant dans chaque classe d'écran.
Une autre approche pourrait être la suivante:
fun Fragment.setToolbarTitle(title: String) {
(activity as NavigationDrawerActivity).supportActionBar?.title = title
}
Vous pouvez utiliser le fichier xml du graphique de navigation et définir le libellé du fragment sur un argument. Ensuite, dans votre fragment parent, vous pouvez passer un argument à l'aide de SafeArgs et fournir une valeur par défaut pour éviter que le titre soit nul ou vide.
<!--set the fragment's title to characters name passed as an argument-->
<fragment
Android:id="@+id/characterDetailFragment2"
Android:name="ui.character.CharacterDetailFragment"
Android:label="{character_name}"
tools:layout="@layout/fragment_character_detail">
<argument
Android:name="character_name"
Android:defaultValue="Name"
app:argType="string" />
</fragment>