So legen Sie den Titel in der App-Leiste mit der Navigationsarchitekturkomponente fest

Lesezeit: 10 Minuten

Benutzer-Avatar
Rajarschi

Ich habe die Navigationsarchitekturkomponente ausprobiert und habe jetzt Schwierigkeiten beim Festlegen des Titels. Wie setze ich den Titel programmgesteuert und wie funktioniert es?

Um meine Frage zu klären, lassen Sie uns ein Beispiel haben, in dem ich eine einfache App mit eingerichtet habe MainActivity Hosting des Navigations-Host-Controllers, der MainFragment hat eine Schaltfläche und beim Klicken auf die Schaltfläche geht es zu DetailFragment.

Derselbe Code aus einer anderen Frage mehrerer App-Leisten bei Stapelüberlauf.

Hauptaktivität

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();
    }
}

HauptFragment

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>

Wenn Sie also meine App starten, der Titel ist “MainActivity”. Wie üblich zeigt es die MainFragment das die Schaltfläche enthält, zu der man gehen soll DetailFragment. In dem DialogFragment Ich habe den Titel wie folgt festgelegt:

getActivity().getSupportActionBar().setTitle("Detail");

Erstes Problem: Klicken Sie also auf die Schaltfläche auf der MainFragment zu gehen DetailFragment, es geht dorthin und der Titel ändert sich zu “Detail”. Aber beim Klicken auf die Zurück-Schaltfläche, die Titel ändert sich zu “fragment_main”. Also habe ich diese Codezeile hinzugefügt MainFragment:

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    // ...

    //Showing the title 
    Navigation.findNavController(view)
      .getCurrentDestination().setLabel("Hello");
}

Nun kehrt die zwar zurück aus DetailFragment zu MainFragment der Titel ändert sich in „Hallo“. Aber hier kommt die zweites Problemwenn ich die App schließe und neu starte, ändert sich der Titel wieder in „MainActivity“, obwohl es stattdessen „Hello“ anzeigen sollte, weißt du?

Ok, dann hinzufügen setTitle("Hello") in MainFrgment funktioniert auch nicht. Beispiel: Die Aktivität beginnt und der Titel lautet „Hallo“, gehen Sie zu DetailsFragment und drücken Sie erneut die Zurück-Taste, geht der Titel zurück zu “fragment_main”.

Die einzige Lösung ist, beides zu haben setTitle("Hello") zusammen mit Navigation.findNavController(view).getCurrentDestination().setLabel("Hello") in MainFragment.

Was ist also der richtige Weg, um den Titel für Fragmente mit der Navigationskomponente anzuzeigen?

  • möglicherweise dupliziert von stackoverflow.com/questions/50599238/…

    – Agna JirKon Rx

    25. Juli 2019 um 22:46 Uhr


  • Beantwortet das deine Frage? Dynamischer ActionBar-Titel aus einem Fragment mit AndroidX-Navigation

    – Adam Hurwitz

    14. Dezember 2020 um 18:15 Uhr

Benutzer-Avatar
ʍѳђઽ૯ท

Es liegt eigentlich daran:

android:label="fragment_main"

Welche hast du in der xml gesetzt.

Was ist also der richtige Weg, um den Titel anzuzeigen? Fragments mit Navigationskomponente?

setTitle() funktioniert an dieser Stelle. Aber, weil Sie Label für diese festlegen Fragments, wird das Label möglicherweise erneut angezeigt, wenn die neu erstellt wird Activity. Die Lösung wird wahrscheinlich das Löschen sein android:label und dann machen Sie Ihre Sachen mit Code:

((AppCompatActivity) getActivity()).getSupportActionBar().setTitle("your title");

Oder:

((AppCompatActivity) getActivity()).getSupportActionBar().setSubtitle("your subtitle");

Im onCreateView().


Abhilfe gefunden:

interface TempToolbarTitleListener {
    fun updateTitle(title: String)
}

class MainActivity : AppCompatActivity(), TempToolbarTitleListener {

    ...

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

Dann:

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

Sehen Sie sich auch das an: Dynamischer ActionBar-Titel aus einem Fragment mit AndroidX-Navigation

  • Ich habe dies bereits versucht und das Ergebnis ist, wenn ich es nicht verwende Navigation.findNavController(view).getCurrentDestination().setLabel("Hello") in dem MainFragment dann bleibt der Titel “Detail”. Und ich weiß nicht warum, jemand hat die Frage abgelehnt: p.

    – Rajarschi

    26. September 2018 um 10:20 Uhr


  • Ich möchte hier klarstellen und hinzufügen setTitle zusammen mit Navigation.findNavController(view).getCurrentDestination().setLabel("Hello") scheint die einzige Möglichkeit zu sein, den Titel zu erhalten, selbst wenn ich die App starte oder die Zurück-Taste drücke. Gibt es also keinen praktikablen Weg mit einer einzigen Codezeile? Ok, lassen Sie mich die Frage aktualisieren.

    – Rajarschi

    26. September 2018 um 10:32 Uhr


  • Es gibt also ein Problem mit der Navigationskomponente.

    – Rajarschi

    26. September 2018 um 11:10 Uhr

  • Manuelles Bearbeiten der Titelleiste oder Übernahme des Inhalts von getCurrentDestination wird es mit dem NavController synchronisieren. Die richtige Lösung finden Sie unter stackoverflow.com/questions/50599238/…

    – gefreut

    31. Dezember 2019 um 1:54 Uhr

Benutzer-Avatar
Rajarschi

Da andere noch an der Beantwortung dieser Frage teilnehmen, lassen Sie mich antworten meine eigene frage da sich die APIs seitdem geändert haben.

Zuerst entfernen android:label aus dem/den Fragment/en, dessen Titel Sie ändern möchten, von innen heraus navigation.xml (auch bekannt als Navigationsdiagramm).

Jetzt können Sie den Titel mit ändern Fragment durch Anruf

(requireActivity() as MainActivity).title = "My title"

Aber die bevorzugte Methode, die Sie verwenden sollten, ist die API NavController.addOnDestinationChangedListener von innen MainActivity. Ein Beispiel:

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"        
//    }
}

  • Falls Sie überhaupt keinen Titel wünschen, können Sie diesen nicht angeben null oder eine leere Zeichenfolge. Stattdessen müssen Sie die festlegen destination.label zu " ". Ich habe dies als Beispiel in Ihren Code eingefügt (sollte nach Peer-Review meiner Änderungen sichtbar sein)

    – mützenflo

    16. August 2019 um 15:00 Uhr

  • @muetzenflo dies wurde ab der Navigations-Benutzeroberfläche “2.3.0-alpha04” behoben.

    – Verbündeter

    2. Mai 2020 um 12:42 Uhr


  • Oder Sie können direkt destination.label festlegen und die AppBar-Beschriftung ändert sich. Das OnDestinationChangedListener muss vor dem Anruf eingestellt werden NavigationUI.setupActionBarWithNavController(...).

    – Vit Kapitola

    22. Juli 2020 um 9:47 Uhr

  • (requireActivity() as MainActivity).title ändert die Beschriftung nicht, selbst nachdem die Beschriftung aus dem Navigationsdiagramm entfernt wurde. Vielleicht hat sich die API-Funktionalität kürzlich geändert.

    – Adam Hurwitz

    13. Dezember 2020 um 2:29 Uhr


  • Oder fügen Sie einfach Beschriftungen zu allen Fragmenten im Navigations-App-Diagramm hinzu und legen Sie fest title = destination.labelwie in: findNavController(R.id.nav_host_fragment).addOnDestinationChangedListener { controller, destination, arguments -> title = destination.label }

    – MiStr

    13. Februar 2021 um 17:04 Uhr


Sie können diesen Code in Ihrem Fragment verwenden, wenn Sie Ihre App-Leiste nicht angeben (Standard-Appbar).

(activity as MainActivity).supportActionBar?.title = "Your Custom Title"

Denken Sie daran, die zu löschen android:label -Attribut in Ihrem Navigationsdiagramm

Fröhlicher Code ^-^

Aus Erfahrung, NavController.addOnDestinationChangedListener

Scheint gut zu funktionieren. Mein Beispiel unten auf meiner MainActivity hat die Magie bewirkt

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"
        }

    }

Heutzutage gibt es einen viel einfacheren Weg, dies mit Kotlin und zu erreichen 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()
}

Das ist es im Grunde. Angeben nicht vergessen android:label in Ihren Navigationsdiagrammen.

Sie können die XML-Datei des Navigationsdiagramms verwenden und die Bezeichnung des Fragments auf ein Argument setzen. Dann können Sie in Ihrem übergeordneten Fragment ein Argument mit SafeArgs übergeben (bitte folgen Sie der Anleitung auf https://developer.android.com/guide/navigation/navigation-pass-data#Safe-args zum Einrichten von SafeArgs) und geben Sie einen Standardwert an, um zu vermeiden, dass der Titel null oder leer ist.

<!--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>

Im Ursprungsfragment:

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 und FragmentBAction – Diese Klassen werden automatisch aus IDs in Ihrer Datei „nav_graph.xml“ generiert. In Klassen vom Typ „Ihre Aktions-ID + Aktion“ finden Sie automatisch generierte Setter-Methoden, mit denen Sie Argumente übergeben können

In Ihrem Empfangsziel rufen Sie die automatisch generierte Klasse {Ihre empfangende Fragment-ID}+Args auf

FragmentBArgs.fromBundle(requireArguments()).getMyTitle();

Bitte beachten Sie den offiziellen Leitfaden unter
https://developer.android.com/guide/navigation/navigation-pass-data#Safe-args

Ich habe ein paar Stunden damit verbracht, eine sehr einfache Sache zu versuchen, wie das Ändern des Balkentitels eines Detailfragments, wenn ich von einem bestimmten Element auf einem Masterfragment navigiere. Mein Daddy hat immer zu mir gesagt: KISS Keep it simple Sohn. setTitle() ist abgestürzt, Schnittstellen sind umständlich, hat eine (sehr) einfache Lösung in Bezug auf Codezeilen für die neueste Implementierung der Fragmentnavigation gefunden. Lösen Sie im Masterfragment den NavController auf, holen Sie sich den NavGraph, finden Sie den Zielknoten, legen Sie den Titel fest und navigieren Sie zu guter Letzt dorthin:

//----------------------------------------------------------------------------------------------

private void navigationToDetail() {

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);

}

  • Danke mann. Es ist sehr effektiv und einfach 🙂

    – Rezaul Islam

    27. Oktober 2021 um 4:09 Uhr

  • Warum nicht einfach Verwenden Sie setTitle richtig? Wenn Sie es in onCreate jedes Fragments setzen, sollte es nicht abstürzen.

    – David

    24. Juni um 20:20 Uhr

1159100cookie-checkSo legen Sie den Titel in der App-Leiste mit der Navigationsarchitekturkomponente fest

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy