So löschen Sie den Navigationsstapel nach dem Navigieren zu einem anderen Fragment in Android

119

Ich verwende die neue Navigationsarchitekturkomponente in Android und kann den Navigationsstapel nach dem Wechsel zu einem neuen Fragment nicht mehr löschen.

Beispiel: Ich bin im loginFragment und möchte, dass dieses Fragment vom Stapel gelöscht wird, wenn ich zum Home-Fragment navigiere, damit der Benutzer nicht zum loginFragment zurückkehrt, wenn er die Zurück-Taste drückt.

Ich verwende einen einfachen NavHostFragment.findNavController (Fragment) .navigate (R.id.homeFragment) zum Navigieren.

Aktueller Code:

mAuth.signInWithCredential(credential)
            .addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment);
                    } else {
                        Log.w(TAG, "signInWithCredential:failure", task.getException());
                    }
                }
            });

Ich habe versucht, die NavOptions in navig () zu verwenden , aber die Schaltfläche "Zurück" sendet mich immer noch zurück zum loginFragment

NavOptions.Builder navBuilder = new NavOptions.Builder();
NavOptions navOptions = navBuilder.setPopUpTo(R.id.homeFragment, false).build();   
             NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment, null, navOptions);
Youssef El Behi
quelle
Sie können verwenden popBackStackoder fügen Sie nicht LoginFragmentbieten Backstack nullauf addToBackStack(null);und ersetzen sie durch neueFragment
Yupi
Bitte bearbeiten Sie Ihren Beitrag und fügen Sie den Code hinzu, mit dem Sie jetzt navigieren
Scheunen
Ich habe meine Frage bearbeitet und meinen Code hinzugefügt
Youssef El Behi
Ich denke, @Yupi hat einen guten Vorschlag gemacht. Oder Sie könnten die navigate()Methode wie verwenden navigate(int resId, Bundle args, NavOptions navOptions)und NavOptionsdiejenige bereitstellen, die am besten zu Ihrem Senario passt
Scheunen
Ich habe versucht, The NavOptions zu verwenden, aber der Zurück-Button sendet mich immer noch zurück zum LoginFragment
Youssef El Behi

Antworten:

175

Fügen Sie zunächst Attribute app:popUpTo='your_nav_graph_id'und app:popUpToInclusive="true"das Aktions-Tag hinzu.

<fragment
    android:id="@+id/signInFragment"
    android:name="com.glee.incog2.android.fragment.SignInFragment"
    android:label="fragment_sign_in"
    tools:layout="@layout/fragment_sign_in" >
    <action
        android:id="@+id/action_signInFragment_to_usersFragment"
        app:destination="@id/usersFragment"
        app:launchSingleTop="true"
        app:popUpTo="@+id/main_nav_graph"
        app:popUpToInclusive="true" />
</fragment>

Zweitens navigieren Sie zum Ziel, wobei Sie die obige Aktion als Parameter verwenden.

findNavController(fragment).navigate(
     SignInFragmentDirections.actionSignInFragmentToUserNameFragment())

Weitere Informationen finden Sie in den Dokumenten .

HINWEIS : Wenn Sie mit der Methode navigieren navigate(@IdRes int resId), erhalten Sie nicht das gewünschte Ergebnis. Daher habe ich Methode verwendet navigate(@NonNull NavDirections directions).

Subhojit Shaw
quelle
1
Beste Antwort, da clearTask jetzt in neuen Versionen der Android-Navigationskomponente veraltet ist!
Michał Ziobro
13
Man kann auch die ID der Aktion wie findNavController(fragment).navigate(R.id.action_signInFragment_to_usersFragment)
folgt verwenden
1
Genial! Jetzt habe ich jedoch das Backup / Back-Symbol, aber das Drücken macht nichts. Was ist ein sauberer Weg, um das auch zu entfernen?
Dänischer Khan
6
Ich denke, der Schlüssel zu dieser Antwort liegt darin, dass "popUpTo" (das alle Fragmente zwischen Ihrem aktuellen Fragment und dem ersten Auftreten der ID entfernt, die Sie an dieses Argument übergeben) auf die ID des Navigationsdiagramms selbst gesetzt wird . Den Rückstapel effektiv vollständig löschen. Diesen Kommentar nur hinzufügen, weil ich nicht gesehen habe, dass er genau so erklärt wurde, und es war das, wonach ich gesucht habe. +1!
Scott O'Toole
7
HINWEIS : Nicht, dass die Klasse "Richtungen" nicht generiert wird, wenn Sie kein Gradle für sichere Argumente angegeben haben. Für weitere Informationen besuchen Sie hier
Jaydip Kalkani
28

In meinem Fall musste ich alles im hinteren Stapel entfernen, bevor ich ein neues Fragment öffnete, also habe ich diesen Code verwendet

navController.popBackStack(R.id.fragment_apps, true);
navController.navigate(R.id.fragment_company);

In der ersten Zeile wird der hintere Stapel entfernt, bis das in meinem Fall angegebene Fragment erreicht ist. Es handelt sich also um das Ausgangsfragment, sodass der gesamte hintere Stapel vollständig entfernt wird. Wenn der Benutzer in die fragment_company zurückklickt, schließt er die App.

lamba
quelle
1
Was ist fragment_apps??
IgorGanapolsky
1
Das aktuelle Fragment. Es ist wie wenn Sie auf fragmentOne sind und fragmentTwo gehen möchten. Zwei müssen auch navController.popBackStack (R.id.fragmentOne, true) verwenden. navController.navigate (R.id.fragmentTwo);
Bbeni
26

Ich denke, Ihre Frage bezieht sich speziell auf die Verwendung des Pop-Verhaltens / Pop To / App: popUpTo (in xml)

In der Dokumentation
Popup zu einem bestimmten Ziel, bevor Sie navigieren. Dadurch werden alle nicht übereinstimmenden Ziele aus dem Backstack entfernt, bis dieses Ziel gefunden wird.

Beispiel (Simple Job Hunting App)
Mein Diagramm start_screen_nav sieht folgendermaßen aus:

startScreenFragment (start) -> loginFragment -> EmployerMainFragment

                            -> loginFragment -> JobSeekerMainFragment

wenn ich navigieren möchten EmployerMainFragmentund Pop alle einschließlich startScreenFragment dann wird der Code sein:

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/startScreenFragment"
            app:popUpToInclusive="true" />

Wenn ich zu EmployerMainFragmentallen navigieren und alle außer ausblenden möchte, lautet startScreenFragmentder Code:

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/startScreenFragment"/>

Wenn ich zu EmployerMainFragmentund Pop navigieren möchte, loginFragmentaber nicht, startScreenFragment lautet der Code:

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/loginFragment"
            app:popUpToInclusive="true"/>

ODER

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/startScreenFragment"/>
Dongkichan
quelle
Wie geht das programmgesteuert statt in XML?
IgorGanapolsky
14

HINWEIS: Die Aufgabe zum Löschen ist veraltet, die offizielle Beschreibung lautet

Diese Methode ist veraltet. Verwenden Sie setPopUpTo (int, boolean) mit der ID des NavController-Diagramms und setzen Sie inklusive auf true.

Alte Antwort

Wenn Sie nicht durch alle , dass Flaum im Code gehen wollen, können Sie einfach überprüfen , Clear Taskin Launch Optionsden Eigenschaften der Aktion.

Startoptionen

Bearbeiten: Ab Android Studio 3.2 Beta 5 ist "Aufgabe löschen" im Fenster "Startoptionen" nicht mehr sichtbar. Sie können sie jedoch im XML-Code der Navigation im Aktions- Tag durch Hinzufügen verwenden

app:clearTask="true"
Mel
quelle
Sie sollten stattdessen die ID des Stammverzeichnisses des Diagramms verwenden, da in der Meldung clearTask xml attributedeprecation steht: "Setzen Sie popUpTo auf das Stammverzeichnis Ihres Diagramms und verwenden Sie popUpToInclusive". Ich habe es so gemacht und das gleiche gewünschte Verhalten mit findNavController () erreicht. Navigieren (@IdRes resId, Bundle)
artnest
Die Verwendung des empfohlenen Ansatzes zum Festlegen von popUpTo und popUpToInclusive scheint nicht für Aktionen zwischen anderen Zielen als dem Startziel zu
funktionieren
Ich stoße tatsächlich auf ein Problem, bei dem es mit dem Startziel nicht funktioniert, was frustrierend ist.
Mel
Wie würden Sie dies tun, wenn Sie das Fragment, in das Sie einfügen möchten, nicht kennen? Zum Beispiel habe ich ein Fragment, das ein neues Element erstellt. Nach dem Erstellen möchte ich die Detailansicht dieses Elements anzeigen. Ich möchte jedoch nicht, dass der Benutzer anschließend zum Erstellungsfragment zurückkehrt, sondern zur vorherigen Ansicht. Dies kann entweder eine Liste aller Elemente oder ein anderes Element sein, auf dem das neue basiert. Ist dann clearTask die einzige Option?
Findusl
Nein, es sollte eigentlich nicht mehr verwendet werden. Stattdessen sollten Sie popUpTo einschließlich verwenden. Ich werde die Antwort sehr bald mit meinen Erkenntnissen zur Verwendung von popUpTo aktualisieren.
Mel
5

Ich finde es endlich heraus, wie man UP in Navigation für ein Fragment mit der neuen Navigationsarchitekturkomponente deaktiviert.

Ich musste .setClearTask (true) als NavOption angeben.

mAuth.signInWithCredential(credential)
            .addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "signInWithCredential:success");


                        NavOptions.Builder navBuilder = new NavOptions.Builder();
                        NavOptions navOptions = navBuilder.setClearTask(true).build();
                        NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment,null,navOptions);
                    } else {
                        Log.w(TAG, "signInWithCredential:failure", task.getException());

                    }

                }
            });
Youssef El Behi
quelle
2
Schön zu sehen, dass Sie meinen Vorschlag zur Verwendung von navigate(int resId, Bundle args, NavOptions navOptions)undNavOptions
Scheunen
Ja. Danke @Barns
Youssef El Behi
14
Ja, "setClearTask" ist veraltet, aber stattdessen können Sie verwenden: val navOptions = NavOptions.Builder().setPopUpTo(R.id.my_current_frag, true).build() findNavController().navigate(R.id.my_next_frag, null, navOptions)
Pablo Reyes
1
@ c-an sollte es funktionieren. Wahrscheinlich verwenden Sie eine falsche Zielfragment-ID (z. B. R.id.my_next_fragLogin oder Begrüßungsbildschirm). Auch wenn Sie nur eine popBack möchten, können Sie verwenden : NavHostFragment.findNavController(this).popBackStack().
Pablo Reyes
2
@PabloReyes Dies ist die richtige Antwort. Geben Sie einfach an, welches Fragment als letztes entfernt werden soll: Angenommen, Sie haben diesen Stapel: Fragment_1 Fragment_2 Fragment_3 Sie befinden sich jetzt auf Fragment_3 und möchten zu Fragment_4 navigieren und alle anderen Fragmente löschen. Geben Sie einfach in der Navigations-XML an: app: popUpTo = "@ id / Fragment_1" app: popUpToInclusive = "true"
user3193413
5

Hier ist, wie ich es mache.

 //here the R.id refer to the fragment one wants to pop back once pressed back from the newly  navigated fragment
 val navOption = NavOptions.Builder().setPopUpTo(R.id.startScorecardFragment, false).build()

//now how to navigate to new fragment
Navigation.findNavController(this, R.id.my_nav_host_fragment)
                    .navigate(R.id.instoredBestPractice, null, navOption)
Abdul Rahman Shamair
quelle
Warum ist Ihre inclusive Flagge false?
IgorGanapolsky
2
Laut Dokumentation inclusive boolean: true to also pop the given destination from the back stack setze ich das Inklusiv auf false, weil ich das aktuelle Fragment nicht vom Stapel entfernen möchte.
Abdul Rahman Shamair
4
    NavController navController 
    =Navigation.findNavController(requireActivity(),          
    R.id.nav_host_fragment);// initialize navcontroller

    if (navController.getCurrentDestination().getId() == 
     R.id.my_current_frag) //for avoid crash
  {
    NavDirections action = 
    DailyInfoSelectorFragmentDirections.actionGoToDestionationFragment();

    //for clear current fragment from stack
    NavOptions options = new 
    NavOptions.Builder().setPopUpTo(R.id.my_current_frag, true).build();
    navController.navigate(action, options);
    }
Emad Pirayesh
quelle
1

Sie können das Zurückdrücken der Basisaktivität wie folgt überschreiben:

override fun onBackPressed() {

  val navigationController = nav_host_fragment.findNavController()
  if (navigationController.currentDestination?.id == R.id.firstFragment) {
    finish()
  } else if (navigationController.currentDestination?.id == R.id.secondFragment) {
    // do nothing
  } else {
    super.onBackPressed()
  }

}
ROHIT LIEN
quelle
1

Hinweis: Dies bezieht sich möglicherweise nicht auf die eigentliche Frage.

Dieser Ansatz ist nützlich, wenn eine einzelne Instanz eines Fragments für die Navigation benötigt wird .

// imports
import androidx.navigation.navOptions

// navigate to fragment
navController.navigate(R.id.nav_single_instance_fragment, null,
                            navOptions {
                                popUpTo = R.id.nav_single_instance_fragment
                                launchSingleTop = true
                            })

Hier navOptionsist DSL und muss importiert werden. Zum Zeitpunkt dieser Antwort verwende ich die folgenden Gradle-Versionen der Navigations-Benutzeroberfläche .

    def nav_version = "2.2.0"
    implementation "androidx.navigation:navigation-ui:$nav_version"
    implementation "androidx.navigation:navigation-fragment:$nav_version"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
Sagar Chapagain
quelle
0

Ich bemühte mich eine Weile, zu verhindern, dass der Zurück-Button zu meinem Startfragment zurückkehrte, was in meinem Fall eine Intro-Nachricht war, die nur einmal erscheinen sollte.

Die einfache Lösung bestand darin, eine globale Aktion zu erstellen, die auf das Ziel verweist, an dem der Benutzer bleiben soll. Sie müssen app:popUpTo="..."richtig einstellen - stellen Sie es auf das Ziel ein, von dem Sie abspringen möchten. In meinem Fall war es meine Intro-Nachricht. Auch eingestelltapp:popUpToInclusive="true"

Tyler
quelle
0

Ich kann dies anhand eines kleinen Beispiels erklären. Ich habe zwei Fragmente Mitarbeiterliste und Mitarbeiter löschen. Beim Löschen eines Mitarbeiters habe ich zwei Klickereignisse abgebrochen und gelöscht. Wenn ich auf Abbrechen klicke, gehe ich einfach zurück zum Bildschirm mit der Mitarbeiterliste. Wenn ich auf Löschen klicke, möchte ich alle vorherigen Backstacks löschen und zum Bildschirm mit der Mitarbeiterliste wechseln.

Zum vorherigen Bildschirm gehen:

<action
        android:id="@+id/action_deleteEmployeeFragment_to_employeesListFragment2"
        app:destination="@id/employeesListFragment"/>


    btn_cancel.setOnClickListener {
                it.findNavController().popBackStack()
            }

Um alle vorherigen Backstacks zu löschen und zum neuen Bildschirm zu wechseln:

<action
        android:id="@+id/action_deleteEmployeeFragment_to_employeesListFragment2"
        app:destination="@id/employeesListFragment"
        app:popUpTo="@id/employeesListFragment"
        app:popUpToInclusive="true"
        app:launchSingleTop="true" />


 btn_submit.setOnClickListener {
                   it.findNavController().navigate(DeleteEmployeeFragmentDirections.actionDeleteEmployeeFragmentToEmployeesListFragment2())
        }

Weitere Informationen: Jetpack-Navigationsbeispiel

Dhrumil Shah
quelle
0

Sie können so einfach machen wie:

getFragmentManager().popBackStack();

Wenn Sie die Anzahl überprüfen möchten, können Sie Folgendes tun:

getFragmentManager().getBackStackEntryCount()
MrGuy
quelle