Wie implementiere ich onBackPressed () in Fragmenten?


459

Gibt es eine Möglichkeit, wie wir onBackPressed()in Android Fragment implementieren können, ähnlich wie wir es in Android Activity implementieren?

Da haben die Fragment Lebenszyklus nicht onBackPressed(). Gibt es eine andere alternative Methode, um onBackPressed()Android 3.0-Fragmente zu überschreiben?


13
IMHO sollte ein Fragment den BACK-Button weder kennen noch interessieren. Eine Aktivität könnte sich um die Schaltfläche ZURÜCK kümmern, allerdings mit Fragmenten, die normalerweise von FragmentManager verarbeitet werden. Da das Fragment jedoch nichts über seine größere Umgebung weiß (z. B. ob es eine von mehreren in der Aktivität ist oder nicht), ist es für ein Fragment nicht wirklich sicher, zu versuchen, die richtige BACK-Funktionalität zu bestimmen.
CommonsWare

61
Wie wäre es, wenn das gesamte Fragment eine WebView anzeigt und Sie möchten, dass die WebView zur vorherigen Seite "zurückkehrt", wenn die Zurück-Taste gedrückt wird?
mharper

Die Antwort von Michael Herbig war perfekt für mich. Aber für diejenigen, die getSupportFragmentManager () nicht verwenden können. Verwenden Sie stattdessen getFragmentManager ().
Dantalian

Die Antwort von Dmitry Zaitsev auf [Ähnliche Frage] [1] funktioniert einwandfrei. [1]: stackoverflow.com/a/13450668/1372866
ashakirov

6
Um mharper zu beantworten, können Sie möglicherweise Folgendes tun: webview.setOnKeyListener (neuer OnKeyListener () {@Override public boolean onKey (Ansicht v, int keyCode, KeyEvent-Ereignis) {if (keyCode == KeyEvent.KEYCODE_BACK && webview.canGoBack ()) {webview.goBack (); return true;} return false;}});
Omid Aminiva

Antworten:


307

Ich habe auf diese Weise das Überschreiben onBackPressedin der Aktivität gelöst . Alle FragmentTransactionsind addToBackStackvor dem Festschreiben:

@Override
public void onBackPressed() {

    int count = getSupportFragmentManager().getBackStackEntryCount();

    if (count == 0) {
        super.onBackPressed();
        //additional code
    } else {
        getSupportFragmentManager().popBackStack();
    }

}


4
Mit count == 1 kann das erste Fragment bei einmaligem Drücken der Zurück-Taste geschlossen werden. Aber sonst die beste Lösung
Kunalxigxag

2
Wenn Sie die Support v7-Bibliothek verwenden und Ihre Aktivität von FragmentActivity (oder einer Unterklasse wie AppCompatActivity) ausgeht, geschieht dies standardmäßig. Siehe FragmentActivity # onBackPressed
Xiao

3
Sie können jedoch keine Variablen, Klassen und Funktionen aus der Klasse des Fragments in diesem Code verwenden
user25

2
@Prabs Wenn Sie das Support v4-Fragment verwenden, stellen Sie sicher, dass Sie getSupportFragmentManager () verwenden. GetBackStackEntryCount ();
Veer3383

151

Meiner Meinung nach ist die beste Lösung:

JAVA-LÖSUNG

Erstellen Sie eine einfache Schnittstelle:

public interface IOnBackPressed {
    /**
     * If you return true the back press will not be taken into account, otherwise the activity will act naturally
     * @return true if your processing has priority if not false
     */
    boolean onBackPressed();
}

Und in deiner Aktivität

public class MyActivity extends Activity {
    @Override public void onBackPressed() {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_container);
       if (!(fragment instanceof IOnBackPressed) || !((IOnBackPressed) fragment).onBackPressed()) {
          super.onBackPressed();
       }
    } ...
}

Endlich in deinem Fragment:

public class MyFragment extends Fragment implements IOnBackPressed{
   @Override
   public boolean onBackPressed() {
       if (myCondition) {
            //action not popBackStack
            return true; 
        } else {
            return false;
        }
    }
}

KOTLIN-LÖSUNG

1 - Schnittstelle erstellen

interface IOnBackPressed {
    fun onBackPressed(): Boolean
}

2 - Bereiten Sie Ihre Aktivität vor

class MyActivity : AppCompatActivity() {
    override fun onBackPressed() {
        val fragment =
            this.supportFragmentManager.findFragmentById(R.id.main_container)
        (fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let {
            super.onBackPressed()
        }
    }
}

3 - Implementieren Sie in Ihrem Zielfragment

class MyFragment : Fragment(), IOnBackPressed {
    override fun onBackPressed(): Boolean {
        return if (myCondition) {
            //action not popBackStack
            true
        } else {
            false
        }
    }
}

1
Was ist R.id.main_container? Ist das die ID für den FragmentPager?
Nathan F.

1
@MaximeJallu in der Kotlin-Lösung kann das Mischen sicherer Aufrufe ( .?) und das Erzwingen von Nicht-Nullen ( !!) zu NPEs führen - die Umwandlung ist nicht immer sicher. Ich würde lieber schreiben if ((fragment as? IOnBackPressed)?.onBackPressed()?.not() == true) { ... }oder mehr kotliney(fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let { ... }
Antek

1
@Antek Hallo, vielen Dank für Ihre Rückkehr, Sie hätten den Beitrag bearbeiten können, um Ihre Korrektur vorzunehmen. Beurteilen Sie die Gesamtlösung nicht streng.
Maxime Jallu

4
Sollte es nicht sein .onBackPressed()?.takeIf { !it }?.let{...}? .not()gibt nur die Umkehrung zurück.
David Miguel

1
val fragment = this.supportFragmentManager.findFragmentById(R.id.flContainer) as? NavHostFragment val currentFragment = fragment?.childFragmentManager?.fragments?.get(0) as? IOnBackPressed currentFragment?.onBackPressed()?.takeIf { !it }?.let{ super.onBackPressed() }Dies ist für Leute, die Kotlin und NavigationController
Pavle Pavlov

97

Laut @HaMMeRed ist die Antwort hier Pseudocode, wie es funktionieren soll. Nehmen wir an, Ihre Hauptaktivität heißt BaseActivityuntergeordnete Fragmente (wie im Beispiel SlidingMenu lib). Hier sind die Schritte:

Zuerst müssen wir eine Schnittstelle und eine Klasse erstellen, die ihre Schnittstelle implementiert, um eine generische Methode zu haben

  1. Klassenschnittstelle erstellen OnBackPressedListener

    public interface OnBackPressedListener {
        public void doBack();
    }
  2. Erstellen Sie eine Klasse, die Fähigkeiten von implementiert OnBackPressedListener

    public class BaseBackPressedListener implements OnBackPressedListener {
        private final FragmentActivity activity;
    
        public BaseBackPressedListener(FragmentActivity activity) {
            this.activity = activity;
        }
    
        @Override
        public void doBack() {
            activity.getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }
    }
  3. Seitdem werden wir an unserem Code BaseActivityund seinen Fragmenten arbeiten

  4. Erstellen Sie einen privaten Listener über Ihrer Klasse BaseActivity

    protected OnBackPressedListener onBackPressedListener;
  5. Methode zum Einstellen des Listeners erstellen BaseActivity

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }
  6. in override so onBackPressedetwas implementieren

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
  7. In Ihrem Fragment in onCreateViewsollten Sie unseren Listener hinzufügen

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        activity = getActivity();
    
        ((BaseActivity)activity).setOnBackPressedListener(new BaseBackPressedListener(activity));
    
        View view = ... ;
    //stuff with view
    
        return view;
    }

Voila, wenn Sie jetzt in Fragment zurückklicken, sollten Sie Ihre benutzerdefinierte On-Back-Methode abfangen.


Wenn mehr als ein Fragment ein Listener ist, wie bestimmen Sie, in onBackPressed()welchem ​​Fragment angezeigt wurde, als die Zurück-Taste gedrückt wurde?
Al Lelopath

2
Sie können den Klick-Listener anstelle des neuen baseBackPressedListener anpassen und dort als anonym aufrufen, um ein eigenes Verhalten festzulegen, etwa so:((BaseActivity)activity).setOnBackPressedListener(new OnBackpressedListener(){ public void doBack() { //...your stuff here }});
deadfish

Vielen Dank, ich habe meine Position dazu geändert. Ich würde jedoch empfehlen, jetzt mit lokalen Broadcast-Nachrichten zu entkoppeln. Ich denke, es würde qualitativ hochwertigere Komponenten ergeben, die stark entkoppelt sind.
HaMMeReD


Das hat perfekt für mich funktioniert, ich
starte

86

Dies hat bei mir funktioniert: https://stackoverflow.com/a/27145007/3934111

@Override
public void onResume() {
    super.onResume();

    if(getView() == null){
        return;
    }

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {

            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
                // handle back button's click listener
                return true;
            }
            return false;
        }
    });
}

2
Hat einwandfrei funktioniert! Dann müssen Sie aber auch die Standardaktion ausführen, indem Sie wahrscheinlich eine weitere IF-Bedingung schreiben. Seitdem habe ich mein slideUpPanel abgesenkt, wenn es erweitert wurde. Also musste ich zuerst einen Check markieren, ob (Panel ist oben) dann .....
sud007

18
Das Problem bei dieser Lösung ist, dass getView () nur die Hauptansicht zurückgibt (und nicht die Unteransicht, wenn sie den Fokus erhalten). Wenn Sie also einen EditText haben und Text eingeben, drücken Sie einmal "Zurück", um die Tastatur auszublenden. Das zweite Drücken von "Zurück" wird vom "EditText" (einer Ansicht selbst) aus gestartet, und diese Methode scheint den "nicht zu fangen". Zurück "Taste dann drücken (da es nur für die Hauptansicht fängt). Soweit ich weiß, können Sie die Tastendruckereignisse in Ihrem "EditText" sowie in anderen Ansichten abfangen. Wenn Sie jedoch ein "komplexes" Formular haben, ist dieses nicht so sauber und wartbar wie es sein könnte.
Pelpotronic

Dies ist eine großartige einfache Lösung für den Albtraum, der Fragmente sind. Wenn Sie ein 'komplexes' Edittext-Formular haben, sollten Sie wahrscheinlich Ihr Projekt löschen und von vorne beginnen. Geben Sie false zurück, wenn Sie das Standardverhalten verwenden möchten.
Geben

65

Wenn Sie diese Art von Funktionalität wünschen, müssen Sie sie in Ihrer Aktivität überschreiben und dann YourBackPressedallen Ihren Fragmenten eine Schnittstelle hinzufügen , die Sie bei jedem Drücken der Zurück-Taste auf das entsprechende Fragment aufrufen.

Bearbeiten: Ich möchte meine vorherige Antwort anhängen.

Wenn ich dies heute tun würde, würde ich eine Sendung oder möglicherweise eine bestellte Sendung verwenden, wenn ich erwarten würde, dass andere Panels im Einklang mit dem Master- / Hauptinhaltspanel aktualisiert werden.

LocalBroadcastManagerin der Support-Bibliothek kann dabei helfen, und Sie senden einfach die Sendung ein onBackPressedund abonnieren Ihre Fragmente, die Sie interessieren. Ich denke, dass Messaging eine entkoppelte Implementierung ist und besser skaliert werden kann, daher wäre es jetzt meine offizielle Implementierungsempfehlung. Verwenden Sie einfach die IntentAktion von 'als Filter für Ihre Nachricht. Senden Sie Ihre neu erstellten ACTION_BACK_PRESSED, senden Sie sie aus Ihrer Aktivität und warten Sie in den entsprechenden Fragmenten darauf.


4
Ziemlich gute Idee! Einige zusätzliche Gedanken: + Es ist wichtig, diese Logik zu realisieren, wenn die Sendung von Aktivitätsfragmenten ausgeht (nicht umgekehrt). OnBackPressed ist nur auf Aktivitätsebene verfügbar. Daher ist es hier entscheidend, das Ereignis dort abzufangen und an alle "hörenden" Fragmente zu senden. + Die Verwendung eines EventBus (wie Otto von Square) macht die Implementierung für einen Fall wie diesen trivial!
Kaushik Gopal

2
Ich habe Square's EventBus noch nicht benutzt, aber ich werde in zukünftigen Produkten sein. Ich denke, es ist eine bessere reine Java-Lösung, und wenn Sie die Android-Bereiche Ihrer Architektur ausschalten können, umso besser.
HaMMeReD

Wie lassen Sie nur die oberen Fragmente das Ereignis behandeln? Zum Beispiel möchten Sie das aktuell angezeigte Fragment zerstören
Eugene

Der Rundfunkempfänger sollte an den Lebenszyklus gebunden sein, wenn Sie diesen verwenden. Wenn er nicht sichtbar ist, sollte er das Ereignis nicht empfangen.
HaMMeReD

Beachten Sie auch, dass LocalBroadcastManagerbestellte Sendungen nicht möglich sind
Xiao

46

Nichts davon ist einfach zu implementieren und funktioniert auch nicht optimal.

Fragmente haben einen Methodenaufruf onDetach, der die Arbeit erledigt.

@Override
    public void onDetach() {
        super.onDetach();
        PUT YOUR CODE HERE
    }

Dies wird den Job machen.


20
Das Problem hierbei ist jedoch, dass Sie nicht wissen, ob das Fragment aufgrund eines Zurückdrückens oder aufgrund einer anderen Aktion, die der Benutzer veranlasst hat, eine andere Aktivität oder ein anderes Fragment auszuführen.
Sunny

7
onDetach () wird aufgerufen, wenn das Fragment nicht mehr an seine Aktivität gebunden ist. Dies wird nach onDestroy () aufgerufen. Obwohl Sie diesen Code erhalten würden, wenn Sie die Zurück-Taste drücken, würden Sie diesen Code auch erhalten, wenn Sie von Fragment zu Fragment wechseln. Sie könnten etwas geschicktes tun, um diese Arbeit zu machen ...
apmartin1991

Funktioniert gut für ein DialogFragment.
CoolMind

2
Vergessen Sie nicht, isRemoving()wie unter stackoverflow.com/a/27103891/2914140 beschrieben hinzuzufügen .
CoolMind

1
das ist falsch. Es kann aus mehreren Gründen aufgerufen werden
Bali

40

Wenn Sie androidx.appcompat:appcompat:1.1.0oder höher verwenden, können Sie OnBackPressedCallbackIhrem Fragment wie folgt ein hinzufügen

requireActivity()
    .onBackPressedDispatcher
    .addCallback(this, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            Log.d(TAG, "Fragment back pressed invoked")
            // Do custom work here    

            // if you want onBackPressed() to be called as normal afterwards
            if (isEnabled) {
                isEnabled = false
                requireActivity().onBackPressed()
            }
        }
    }
)

Siehe https://developer.android.com/guide/navigation/navigation-custom-back


Wenn Sie verwenden androidx-core-ktx, können Sie verwendenrequireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { /* code to be executed when back is pressed */ }
Max

Es ist wichtig zu beachten, dass der LifecycleOwnerParameter wie in diesem Beispiel hinzugefügt werden sollte. Ohne sie werden alle danach gestarteten Fragmente aufgerufen, handleBackPressed()wenn die Zurück-Taste gedrückt wird.
Eric B.

muss diese Methode mit der Navigationsbibliothek?
Stille

25

Fügen addToBackStackSie einfach Folgendes hinzu, während Sie zwischen Ihren Fragmenten wechseln:

fragmentManager.beginTransaction().replace(R.id.content_frame,fragment).addToBackStack("tag").commit();

Wenn Sie schreiben addToBackStack(null), wird es von selbst behandelt, aber wenn Sie ein Tag geben, sollten Sie es manuell behandeln.


Muss noch etwas getan werden oder reicht das Hinzufügen der addToBackStack-Methode aus?
Sreecharan Desabattula

1
Wenn Sie nur hinzufügen, dass es funktionieren sollte, sollte es etwas in Ihrem Code enthalten, wenn es immer noch nicht funktioniert. Lassen Sie den Code irgendwo zu sehen, bitte @SreecharanDesabattula
Rudi


20

Da diese Frage und einige der Antworten älter als fünf Jahre sind, möchte ich meine Lösung mitteilen. Dies ist eine Fortsetzung und Modernisierung der Antwort von @oyenigun

AKTUALISIEREN: Am Ende dieses Artikels habe ich eine alternative Implementierung hinzugefügt, die eine abstrakte Fragmenterweiterung verwendet, die die Aktivität überhaupt nicht einbezieht. Dies ist nützlich für alle Benutzer mit einer komplexeren Fragmenthierarchie, die verschachtelte Fragmente umfasst, die ein anderes Back-Verhalten erfordern.

Ich musste dies implementieren, da einige der von mir verwendeten Fragmente kleinere Ansichten haben, die ich mit der Schaltfläche "Zurück" schließen möchte, z. B. kleine Informationsansichten, die angezeigt werden usw. Dies ist jedoch gut für alle, die das Verhalten von überschreiben müssen der Zurück-Knopf in Fragmenten.

Definieren Sie zunächst eine Schnittstelle

public interface Backable {
    boolean onBackPressed();
}

Diese Schnittstelle, die ich aufrufe Backable(ich bin ein Stickler für Namenskonventionen), hat eine einzige Methode onBackPressed(), die einen booleanWert zurückgeben muss. Wir müssen einen booleschen Wert erzwingen, da wir wissen müssen, ob das Drücken der Zurück-Taste das Back-Ereignis "absorbiert" hat. Zurückgeben truebedeutet, dass dies der Fall ist und keine weitere Aktion erforderlich ist. Andernfalls muss falsedie Standard-Zurückaktion weiterhin ausgeführt werden. Diese Schnittstelle sollte eine eigene Datei sein (vorzugsweise in einem separaten Paket mit dem Namen interfaces). Denken Sie daran, dass es eine gute Praxis ist, Ihre Klassen in Pakete zu unterteilen.

Zweitens finden Sie das obere Fragment

Ich habe eine Methode erstellt, die das letzte FragmentObjekt im Backstack zurückgibt. Ich verwende Tags ... Wenn Sie IDs verwenden, nehmen Sie die erforderlichen Änderungen vor. Ich habe diese statische Methode in einer Utility-Klasse, die sich mit Navigationszuständen usw. befasst, aber natürlich platzieren Sie sie dort, wo sie am besten zu Ihnen passt. Zur Erbauung habe ich meine in eine Klasse namens gesetzt NavUtils.

public static Fragment getCurrentFragment(Activity activity) {
    FragmentManager fragmentManager = activity.getFragmentManager();
    if (fragmentManager.getBackStackEntryCount() > 0) {
        String lastFragmentName = fragmentManager.getBackStackEntryAt(
                fragmentManager.getBackStackEntryCount() - 1).getName();
        return fragmentManager.findFragmentByTag(lastFragmentName);
    }
    return null;
}

Stellen Sie sicher, dass die Anzahl der Backstacks größer als 0 ist, da dies sonst ArrayOutOfBoundsExceptionzur Laufzeit ausgelöst werden kann. Wenn es nicht größer als 0 ist, geben Sie null zurück. Wir werden später nach einem Nullwert suchen ...

Drittens in einem Fragment implementieren

Implementieren Sie die BackableSchnittstelle in dem Fragment, in dem Sie das Verhalten der Zurück-Schaltfläche überschreiben müssen. Fügen Sie die Implementierungsmethode hinzu.

public class SomeFragment extends Fragment implements 
        FragmentManager.OnBackStackChangedListener, Backable {

...

    @Override
    public boolean onBackPressed() {

        // Logic here...
        if (backButtonShouldNotGoBack) {
            whateverMethodYouNeed();
            return true;
        }
        return false;
    }

}

onBackPressed()Geben Sie beim Überschreiben die gewünschte Logik ein. Wenn Sie möchten, dass die Zurück-Schaltfläche den Back-Stack nicht öffnet (Standardverhalten), geben Sie true zurück , damit Ihr Back-Ereignis absorbiert wurde. Andernfalls geben Sie false zurück.

Zuletzt in Ihrer Aktivität ...

Überschreiben Sie die onBackPressed()Methode und fügen Sie diese Logik hinzu:

@Override
public void onBackPressed() {

    // Get the current fragment using the method from the second step above...
    Fragment currentFragment = NavUtils.getCurrentFragment(this);

    // Determine whether or not this fragment implements Backable
    // Do a null check just to be safe
    if (currentFragment != null && currentFragment instanceof Backable) {

        if (((Backable) currentFragment).onBackPressed()) {
            // If the onBackPressed override in your fragment 
            // did absorb the back event (returned true), return
            return;
        } else {
            // Otherwise, call the super method for the default behavior
            super.onBackPressed();
        }
    }

    // Any other logic needed...
    // call super method to be sure the back button does its thing...
    super.onBackPressed();
}

Wir erhalten das aktuelle Fragment im Backstack, führen dann eine Nullprüfung durch und stellen fest, ob es unsere BackableSchnittstelle implementiert . Wenn dies der Fall ist, stellen Sie fest, ob das Ereignis absorbiert wurde. Wenn ja, sind wir fertig onBackPressed()und können zurückkehren. Andernfalls behandeln Sie es als normales Zurückdrücken und rufen Sie die Supermethode auf.

Zweite Option, die Aktivität nicht einzubeziehen

Manchmal möchten Sie nicht, dass die Aktivität dies überhaupt behandelt, und Sie müssen sie direkt innerhalb des Fragments behandeln. Aber wer sagt, dass Sie keine Fragmente mit einer Backpress-API haben können? Erweitern Sie einfach Ihr Fragment auf eine neue Klasse.

Erstellen Sie eine abstrakte Klasse, die Fragment erweitert und die View.OnKeyListnerSchnittstelle implementiert ...

import android.app.Fragment;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;

public abstract class BackableFragment extends Fragment implements View.OnKeyListener {

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        view.setFocusableInTouchMode(true);
        view.requestFocus();
        view.setOnKeyListener(this);
    }

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_UP) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                onBackButtonPressed();
                return true;
            }
        }

        return false;
    }

    public abstract void onBackButtonPressed();
}

Wie Sie sehen können, erfasst jedes erweiterte Fragment BackableFragmentautomatisch Rückklicks über die View.OnKeyListenerBenutzeroberfläche. Rufen Sie einfach die abstrakte onBackButtonPressed()Methode innerhalb der implementierten onKey()Methode mit der Standardlogik auf, um einen Zurück-Tastendruck zu erkennen. Wenn Sie andere Tastenklicks als die Schaltfläche "Zurück" registrieren müssen, rufen Sie die superMethode beim Überschreiben onKey()in Ihrem Fragment auf. Andernfalls wird das Verhalten in der Abstraktion überschrieben .

Einfach zu bedienen, einfach erweitern und implementieren:

public class FragmentChannels extends BackableFragment {

    ...

    @Override
    public void onBackButtonPressed() {
        if (doTheThingRequiringBackButtonOverride) {
            // do the thing
        } else {
            getActivity().onBackPressed();
        }
    }

    ...
}

Da die onBackButtonPressed()Methode in der Superklasse abstrakt ist, müssen Sie sie implementieren, sobald Sie sie erweitern onBackButtonPressed(). Es kehrt zurück, voidweil es nur eine Aktion innerhalb der Fragmentklasse ausführen muss und die Absorption der Presse nicht an die Aktivität zurücksenden muss. Stellen Sie sicher, dass Sie die Aktivitätsmethode aufrufen onBackPressed(), wenn für alles, was Sie mit der Zurück-Schaltfläche tun, keine Behandlung erforderlich ist. Andernfalls wird die Zurück-Schaltfläche deaktiviert ... und das möchten Sie nicht!

Vorsichtsmaßnahmen Wie Sie sehen können, wird der Key Listener auf die Stammansicht des Fragments gesetzt, und wir müssen es fokussieren. Wenn in Ihrem Fragment Bearbeitungstexte (oder andere Ansichten, die den Fokus stehlen) enthalten sind, die diese Klasse erweitern (oder andere innere Fragmente oder Ansichten, die dieselbe haben), müssen Sie dies separat behandeln. Es gibt einen guten Artikel über die Erweiterung eines EditText , um den Fokus auf ein Zurückdrücken zu verlieren.

Ich hoffe, jemand findet das nützlich. Viel Spaß beim Codieren.


Danke, es ist eine schöne Lösung. Können Sie sagen, warum Sie super.onBackPressed();zweimal anrufen ?
CoolMind

Es wird nur einmal pro Szenario in Bezug auf den Nullstatus von aufgerufen currentFragment. Wenn das Fragment nicht null ist und das Fragment die BackableSchnittstelle implementiert , wird keine Aktion ausgeführt. Wenn das Fragment nicht null ist und NICHT implementiert wird Backable, rufen wir die Super-Methode auf. Wenn das Fragment null ist, springt es zum letzten Superaufruf.
Mwieczorek

Entschuldigung, ich habe es nicht verstanden. In dem Fall, in dem a currentFragmentnicht null ist und eine Instanz von Backable ist und getrennt wird (wir haben die backTaste gedrückt und das Fragment geschlossen), wird das erste Vorkommen von super.onBackPressed();aufgerufen, dann das zweite.
CoolMind

Ausgezeichnete Lösung
David Toledo

Vielleicht klingt "Closeable" ein bisschen besser als "Backable"
Pumnao

15

Die Lösung ist einfach:

1) Wenn Sie eine Basisfragmentklasse haben, die alle Fragmente erweitern, fügen Sie diesen Code zu ihrer Klasse hinzu, andernfalls erstellen Sie eine solche Basisfragmentklasse

 /* 
 * called when on back pressed to the current fragment that is returned
 */
 public void onBackPressed()
 {
    // add code in super class when override
 }

2) Überschreiben Sie in Ihrer Aktivitätsklasse onBackPressed wie folgt:

private BaseFragment _currentFragment;

@Override
public void onBackPressed()
{
      super.onBackPressed();
     _currentFragment.onBackPressed();
}

3) Fügen Sie in Ihrer Fragment-Klasse den gewünschten Code hinzu:

 @Override
 public void onBackPressed()
 {
    setUpTitle();
 }


9

onBackPressed() bewirkt, dass sich das Fragment von der Aktivität löst.

Laut der Antwort von @Sterling Diaz denke ich, dass er Recht hat. ABER eine Situation wird falsch sein. (zB Bildschirm drehen)

Ich denke also, wir könnten herausfinden, ob wir isRemoving()Ziele erreichen sollen.

Sie können es bei onDetach()oder schreiben onDestroyView(). Es ist Arbeit.

@Override
public void onDetach() {
    super.onDetach();
    if(isRemoving()){
        // onBackPressed()
    }
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    if(isRemoving()){
        // onBackPressed()
    }
}

8

Nun, ich habe es so gemacht und es funktioniert für mich

Einfache Schnittstelle

FragmentOnBackClickInterface.java

public interface FragmentOnBackClickInterface {
    void onClick();
}

Beispielimplementierung

MyFragment.java

public class MyFragment extends Fragment implements FragmentOnBackClickInterface {

// other stuff

public void onClick() {
       // what you want to call onBackPressed?
}

dann überschreiben Sie einfach onBackPressed in Aktivität

    @Override
public void onBackPressed() {
    int count = getSupportFragmentManager().getBackStackEntryCount();
    List<Fragment> frags = getSupportFragmentManager().getFragments();
    Fragment lastFrag = getLastNotNull(frags);
    //nothing else in back stack || nothing in back stack is instance of our interface
    if (count == 0 || !(lastFrag instanceof FragmentOnBackClickInterface)) {
        super.onBackPressed();
    } else {
        ((FragmentOnBackClickInterface) lastFrag).onClick();
    }
}

private Fragment getLastNotNull(List<Fragment> list){
    for (int i= list.size()-1;i>=0;i--){
        Fragment frag = list.get(i);
        if (frag != null){
            return frag;
        }
    }
    return null;
}

Funktioniert nicht ! super.onbackpressed () wird immer aufgerufen :(
Animesh Mangla

So übergeben Sie dieses Ereignis Inner Fragment. Ich habe ViewPager in Aktivität und dieser ViewPager hat Fragmente. Jetzt haben alle diese Fragmente ein untergeordnetes Fragment. So übergeben Sie das Schaltflächenereignis an diese untergeordneten Fragmente.
Kishan Vaghela

@ KishanVaghela Nun, ich habe Android seitdem nicht mehr berührt, aber ich habe eine Idee. Gib mir den 24. Platz, um meine Antwort zu aktualisieren.
Błażej

Ok, kein Problem. Eine Option ist, dass ich jedem untergeordneten Fragment vom übergeordneten Fragment ein Listener-Ereignis übergeben muss. aber es ist kein idealer Weg, weil ich dies für jedes Fragment tun muss.
Kishan Vaghela

@ KishanVaghela Ich habe über Manager in Aktivität nachgedacht. Mit dieser Schnittstelle können Sie Fragmente hinzufügen / entfernen. Bei diesem Ansatz müssen Sie jedoch den Manager in jedem Fragment implementieren, das Unterfragmente enthält. Vielleicht ist es eine bessere Lösung, einen Manager als Singleton zu erstellen. Dann müssen Sie Fragmente / Objekte hinzufügen / entfernen und in 'onBackPressed' rufen Sie die Methode 'onBackPressed' dieses Managers auf. Diese Methode ruft die Methode 'onClick' in jedem Objekt auf, das dem Manager hinzugefügt wurde. Ist dir das mehr oder weniger klar?
Błażej

8

Sie sollten Ihrem Projekt eine Schnittstelle wie unten hinzufügen.

public interface OnBackPressed {

     void onBackPressed();
}

Und dann sollten Sie diese Schnittstelle auf Ihrem Fragment implementieren.

public class SampleFragment extends Fragment implements OnBackPressed {

    @Override
    public void onBackPressed() {
        //on Back Pressed
    }

}

Und Sie können dieses onBackPressed-Ereignis unter Ihren Aktivitäten onBackPressed-Ereignis wie unten auslösen.

public class MainActivity extends AppCompatActivity {
       @Override
        public void onBackPressed() {
                Fragment currentFragment = getSupportFragmentManager().getFragments().get(getSupportFragmentManager().getBackStackEntryCount() - 1);
                if (currentFragment instanceof OnBackPressed) {  
                    ((OnBackPressed) currentFragment).onBackPressed();
                }
                super.onBackPressed();
        }
}

Es ist eine gute Methode, aber seien Sie vorsichtig, rufen getActivity().onBackPressed();Sie nicht auf, da dies die Ausnahme "java.lang.StackOverflowError: Stapelgröße 8 MB" auslöst.
CoolMind

8

Dies ist nur ein kleiner Code, der den Trick macht:

 getActivity().onBackPressed();

Hoffe es hilft jemandem :)


4
Er bat darum, das Verhalten außer Kraft zu setzen und nicht einfach die Aktivitätsmethode aufzurufen.
Mwieczorek

2
Ich habe es sorgfältig gelesen, danke. Ihre Lösung leitet die onBackPressed-Methode einfach an die Aktivität zurück, als er gefragt hat, wie sie überschrieben werden soll.
Mwieczorek

7

Wenn Sie EventBus verwenden, ist dies wahrscheinlich eine weitaus einfachere Lösung:

In deinem Fragment:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    EventBus.getDefault().register(this);
}

@Override
public void onDetach() {
    super.onDetach();
    EventBus.getDefault().unregister(this);
}


// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    getSupportFragmentManager().popBackStack();
}

und in Ihrer Aktivitätsklasse können Sie Folgendes definieren:

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    super.onBackPressed();
}

@Override
public void onBackPressed() {
    EventBus.getDefault().post(new BackPressedMessage(true));
}

BackPressedMessage.java ist nur ein POJO-Objekt

Dies ist super sauber und es gibt keine Probleme mit der Schnittstelle / Implementierung.


7

Das ist meine Lösung:

in MyActivity.java:

public interface OnBackClickListener {
        boolean onBackClick();
    }

    private OnBackClickListener onBackClickListener;

public void setOnBackClickListener(OnBackClickListener onBackClickListener) {
        this.onBackClickListener = onBackClickListener;
    }

@Override
    public void onBackPressed() {
        if (onBackClickListener != null && onBackClickListener.onBackClick()) {
            return;
        }
        super.onBackPressed();
    }

und in Fragment:

((MyActivity) getActivity()).setOnBackClickListener(new MyActivity.OnBackClickListener() {
    @Override
    public boolean onBackClick() {
        if (condition) {
            return false;
        }

        // some codes

        return true;
    }
});

5

Mit der Navigationskomponente können Sie dies folgendermaßen tun :

Java

public class MyFragment extends Fragment {

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // This callback will only be called when MyFragment is at least Started.
    OnBackPressedCallback callback = new OnBackPressedCallback(true /* enabled by default */) {
        @Override
        public void handleOnBackPressed() {
            // Handle the back button event
        }
    });
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);

    // The callback can be enabled or disabled here or in handleOnBackPressed()
}
...
}

Kotlin

class MyFragment : Fragment() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // This callback will only be called when MyFragment is at least Started.
    val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
        // Handle the back button event
    }

    // The callback can be enabled or disabled here or in the lambda
}
...
}

4

Wie wäre es mit onDestroyView ()?

@Override
public void onDestroyView() {
    super.onDestroyView();
}

5
Was passiert mit Ihrer Methode, wenn Sie von einem tatsächlichen Fragment zu einem anderen Fragment wechseln? :)
Choletski

Die nutzloseste Antwort. Es ist das gleiche wie schreiben i = ioder if (1 < 0) {}.
CoolMind

4
@Override
public void onResume() {
    super.onResume();

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
                // handle back button
                replaceFragmentToBackStack(getActivity(), WelcomeFragment.newInstance(bundle), tags);

                return true;
            }

            return false;
        }
    });
}

Nur wenn Fragmente keine fokussierbare Ansicht haben (z. B. Text bearbeiten), lassen Sie Fragmente die Zurück-Schaltfläche selbst wie in dieser Antwort behandeln. Andernfalls lassen Sie Aktivitäten, die Fragmente enthalten, dies mit OnBackPress () tun, während die erste Antwort geschrieben wird. da die fokussierbare Ansicht den Fokus des Fragments stiehlt. Wir können jedoch den Fokus für Fragmente wiedererlangen, indem wir alle fokussierbaren Ansichten mit der Methode clearFocus () klar fokussieren und auf Fragment neu fokussieren.
Nguyen Tan Dat

4
public class MyActivity extends Activity {

    protected OnBackPressedListener onBackPressedListener;

    public interface OnBackPressedListener {
        void doBack();
    }

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
    } 

    @Override
    protected void onDestroy() {
        onBackPressedListener = null;
        super.onDestroy();
    }
}

Fügen Sie in Ihrem Fragment Folgendes hinzu. Vergessen Sie nicht, die Schnittstelle von mainactivity zu implementieren.

public class MyFragment extends Framgent implements MyActivity.OnBackPressedListener {
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
         ((MyActivity) getActivity()).setOnBackPressedListener(this);
    }

@Override
public void doBack() {
    //BackPressed in activity will call this;
}

}

4

In meiner Lösung (Kotlin);

Ich verwende die Funktion onBackAlternative als Parameter für BaseActivity .

BaseActivity

abstract class BaseActivity {

    var onBackPressAlternative: (() -> Unit)? = null

    override fun onBackPressed() {
        if (onBackPressAlternative != null) {
            onBackPressAlternative!!()
        } else {
            super.onBackPressed()
        }
    }
}

Ich habe eine Funktion zum Setzen von onBackPressAlternative auf BaseFragment .

BaseFragment

abstract class BaseFragment {

     override fun onStart() {
        super.onStart()
        ...
        setOnBackPressed(null) // Add this
     }

      //Method must be declared as open, for overriding in child class
     open fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
         (activity as BaseActivity<*, *>).onBackPressAlternative = onBackAlternative
     }
}

Dann kann mein onBackPressAlternative für Fragmente verwendet werden.

Unterfragmente

override fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
    (activity as BaseActivity<*, *>).onBackPressAlternative = {
        // TODO Your own custom onback function 
    }
}

3

Implementieren Sie die Methode ft.addToBackStack () nicht, damit Ihre Aktivität beendet wird, wenn Sie die Zurück-Taste drücken.

proAddAccount = new ProfileAddAccount();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, proAddAccount);
//fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();


3

Befolgen Sie einfach diese Schritte:

Immer beim Hinzufügen eines Fragments,

fragmentTransaction.add(R.id.fragment_container, detail_fragment, "Fragment_tag").addToBackStack(null).commit();

Überschreiben Sie dann in der Hauptaktivität onBackPressed()

if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
    getSupportFragmentManager().popBackStack();
} else {
    finish();
}

Um die Zurück-Schaltfläche in Ihrer App zu handhaben,

Fragment f = getActivity().getSupportFragmentManager().findFragmentByTag("Fragment_tag");
if (f instanceof FragmentName) {
    if (f != null) 
        getActivity().getSupportFragmentManager().beginTransaction().remove(f).commit()               
}

Das ist es!


3

Im Aktivitätslebenszyklus behandelt die Android-Schaltfläche "Zurück" immer FragmentManager-Transaktionen, wenn FragmentActivity oder AppCompatActivity verwendet werden.

Um den Backstack zu handhaben, müssen wir nicht die Anzahl der Backstacks behandeln oder irgendetwas markieren, aber wir sollten den Fokus behalten, während wir ein Fragment hinzufügen oder ersetzen. Hier finden Sie die folgenden Ausschnitte, um die Fälle mit den Zurück-Tasten zu behandeln:

    public void replaceFragment(Fragment fragment) {

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
        }
        transaction.replace(R.id.activity_menu_fragment_container, fragment).commit();
    }

Hier füge ich keinen Backstack für mein Home-Fragment hinzu, da es sich um die Homepage meiner Anwendung handelt. Wenn Sie addToBackStack zu HomeFragment hinzufügen, wartet die App darauf, alle Framente in der Aktivität zu entfernen. Dann wird ein leerer Bildschirm angezeigt, sodass ich die folgende Bedingung behalte:

if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
}

Jetzt können Sie das zuvor hinzugefügte Fragment auf acitvity sehen und die App wird beendet, wenn Sie HomeFragment erreichen. Sie können sich auch die folgenden Schnipsel ansehen.

@Override
public void onBackPressed() {

    if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) {
        closeDrawer();
    } else {
        super.onBackPressed();
    }
}

3

Fragment: Erstellen Sie ein BaseFragment, indem Sie eine Methode platzieren:

 public boolean onBackPressed();

Aktivität:

@Override
public void onBackPressed() {
    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    if (fragments != null) {
        for (Fragment fragment : fragments) {
            if (!fragment.isVisible()) continue;

            if (fragment instanceof BaseFragment && ((BaseFragment) fragment).onBackPressed()) {
                return;
            }
        }
    }

    super.onBackPressed();
}

Ihre Aktivität wird über die angehängten und sichtbaren Fragmente ausgeführt und ruft onBackPressed () für jedes von ihnen auf und bricht ab, wenn eines von ihnen 'true' zurückgibt (was bedeutet, dass es behandelt wurde, also keine weiteren Aktionen).


Sir, dies ist der eleganteste Weg und funktioniert perfekt!
Asozcan

3

Nach dem AndroidX Release Notes , androidx.activity 1.0.0-alpha01wird freigegeben und führt ComponentActivityeine neue Basisklasse der bestehenden FragmentActivityund AppCompatActivity. Und diese Version bringt uns eine neue Funktion:

Sie können jetzt ein OnBackPressedCallbackVia registrieren addOnBackPressedCallback, um onBackPressed()Rückrufe zu erhalten, ohne die Methode in Ihrer Aktivität überschreiben zu müssen.


Können Sie ein Beispiel dafür zeigen? Ich bekomme diese Methode nicht zu lösen.
Bryan Bryce

1
@BryanBryce Ich habe kein Beispiel gefunden, aber das offizielle Dokument: developer.android.com/reference/androidx/activity/…
TonnyL

Die Methode löst b / c nicht auf. AppCompatActivity erstreckt sich tatsächlich von androidx.core.app.ComponentActivity anstelle von androidx.activity.ComponentActivity.
Wooldridgetm

3

Sie können Fragmente in Aktivitäten registrieren, um das Zurückdrücken zu handhaben:

interface BackPressRegistrar {
    fun registerHandler(handler: BackPressHandler)
    fun unregisterHandler(handler: BackPressHandler)
}

interface BackPressHandler {
    fun onBackPressed(): Boolean
}

Verwendungszweck:

Im Fragment:

private val backPressHandler = object : BackPressHandler {
    override fun onBackPressed(): Boolean {
        showClosingWarning()
        return false
    }
}

override fun onResume() {
    super.onResume()
    (activity as? BackPressRegistrar)?.registerHandler(backPressHandler)
}

override fun onStop() {
    (activity as? BackPressRegistrar)?.unregisterHandler(backPressHandler)
    super.onStop()
}

In Aktivität:

class MainActivity : AppCompatActivity(), BackPressRegistrar {


    private var registeredHandler: BackPressHandler? = null
    override fun registerHandler(handler: BackPressHandler) { registeredHandler = handler }
    override fun unregisterHandler(handler: BackPressHandler) { registeredHandler = null }

    override fun onBackPressed() {
        if (registeredHandler?.onBackPressed() != false) super.onBackPressed()
    }
}

3

Das Bereitstellen einer benutzerdefinierten Rückennavigation durch Handling von onBackPressed ist jetzt mit Rückrufen innerhalb des Fragments einfacher.

class MyFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val onBackPressedCallback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (true == conditionForCustomAction) {
                    myCustomActionHere()
                } else  NavHostFragment.findNavController(this@MyFragment).navigateUp();    
        }
    }
    requireActivity().onBackPressedDispatcher.addCallback(
        this, onBackPressedCallback
    )
    ...
}

Wenn Sie die Standard-Rückaktion basierend auf einer bestimmten Bedingung möchten, können Sie Folgendes verwenden:

 NavHostFragment.findNavController(this@MyFragment).navigateUp();

3

Fügen Sie in der onCreate-Methode des Fragments Folgendes hinzu:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    OnBackPressedCallback callback = new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            //Handle the back pressed
        }
    };
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
}

Nach dem Hinzufügen dieser myFragment-Backpress funktioniert, aber jetzt hat die Aktivität Backpress nicht funktioniert
Sunil Chaudhary

2

Beste Lösung,

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity {
    @Override
    public void onBackPressed() {

        FragmentManager fm = getSupportFragmentManager();
        for (Fragment frag : fm.getFragments()) {
            if (frag == null) {
                super.onBackPressed();
                finish();
                return;
            }
            if (frag.isVisible()) {
                FragmentManager childFm = frag.getChildFragmentManager();
                if (childFm.getFragments() == null) {
                    super.onBackPressed();
                    finish();
                    return;
                }
                if (childFm.getBackStackEntryCount() > 0) {
                    childFm.popBackStack();
                    return;
                }
                else {

                    fm.popBackStack();
                    if (fm.getFragments().size() <= 1) {
                        finish();
                    }
                    return;
                }

            }
        }
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.