Come implementare onBackPressed () in Fragments?


460

Esiste un modo in cui possiamo implementare onBackPressed()in Android Fragment simile al modo in cui implementiamo in Attività Android?

Poiché il ciclo di vita del frammento non ha onBackPressed(). Esiste un altro metodo alternativo per superare onBackPressed()i frammenti di Android 3.0?


13
IMHO, un frammento non dovrebbe né conoscere né preoccuparsi del pulsante INDIETRO. A un'attività potrebbe interessare il pulsante INDIETRO, sebbene con frammenti che sono normalmente gestiti da FragmentManager. Ma dal momento che il frammento non è a conoscenza del suo ambiente più ampio (ad esempio, indipendentemente dal fatto che sia o meno uno dei tanti nell'attività), non è davvero sicuro che un frammento stia cercando di determinare quale sia la corretta funzionalità BACK.
Commons War il

61
Che ne dici di quando tutto il frammento lo fa per visualizzare un WebView e vuoi che il WebView "torni" alla pagina precedente quando viene premuto il pulsante Indietro?
Mharper,

La risposta di Michael Herbig è stata perfetta per me. Ma per coloro che non possono usare getSupportFragmentManager (). Utilizzare invece getFragmentManager ().
Dantalian,

La risposta di Dmitry Zaitsev su [Domanda simile] [1] funziona bene. [1]: stackoverflow.com/a/13450668/1372866
ashakirov

6
per rispondere a mharper potrebbe essere che puoi fare qualcosa come webview.setOnKeyListener (new OnKeyListener () {@Override public boolean onKey (Visualizza v, int keyCode, KeyEvent event) {if (keyCode == KeyEvent.KEYCODE_BACK && webview.canGoBack ()) {webview.goBack (); return true;} return false;}});
Omid Aminiva,

Risposte:


307

Ho risolto in questo modo l'override onBackPressednell'attività. Tutti FragmentTransactionsono addToBackStackprima del commit:

@Override
public void onBackPressed() {

    int count = getSupportFragmentManager().getBackStackEntryCount();

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

}


4
count == 1 consentirà di chiudere il primo frammento alla pressione del singolo pulsante indietro. Ma per il resto la soluzione migliore
Kunalxigxag,

2
Se stai utilizzando la libreria di supporto v7 e la tua attività si estende da FragmentActivity (o da una sottoclasse, come AppCompatActivity), ciò accadrà per impostazione predefinita. Vedi FragmentActivity # onBackPressed
Xiao

3
ma non puoi usare variabili, classi e funzioni della classe del frammento in questo codice
user25

2
@Prabs se si utilizza il frammento di supporto v4, assicurarsi di utilizzare getSupportFragmentManager (). GetBackStackEntryCount ();
Veer3383,

152

Secondo me la soluzione migliore è:

SOLUZIONE JAVA

Crea un'interfaccia semplice:

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

E nella tua attività

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

Finalmente nel tuo frammento:

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

SOLUZIONE KOTLIN

1 - Crea interfaccia

interface IOnBackPressed {
    fun onBackPressed(): Boolean
}

2 - Prepara la tua attività

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

3 - Implementa nel tuo frammento di destinazione

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

1
Che cosa è R.id.main_container? È l'ID per FragmentPager?
Nathan F.

1
@MaximeJallu nella soluzione Kotlin, mescolando chiamate sicure ( .?) e applicando non null ( !!) può portare a NPE - il cast non è sempre sicuro. Preferirei scrivere if ((fragment as? IOnBackPressed)?.onBackPressed()?.not() == true) { ... }o più kotliney(fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let { ... }
Antek il

1
@Antek Ciao, grazie per il tuo ritorno, avresti potuto modificare il post, per apportare la tua correzione. Non giudicare severamente la soluzione globale.
Maxime Jallu,

4
Non dovrebbe essere .onBackPressed()?.takeIf { !it }?.let{...}? .not()restituisce semplicemente l'inverso.
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() }Questo è per le persone che usano Kotlin e NavigationController
Pavle Pavlov l'

97

Secondo la risposta di @HaMMeRed qui è pseudocodice come dovrebbe funzionare. Diciamo che viene chiamata la tua attività principale BaseActivityche ha frammenti secondari (come nell'esempio lib di SlidingMenu). Ecco i passaggi:

Per prima cosa dobbiamo creare un'interfaccia e una classe che implementano la sua interfaccia per avere un metodo generico

  1. Crea un'interfaccia di classe OnBackPressedListener

    public interface OnBackPressedListener {
        public void doBack();
    }
  2. Crea una classe che implementa le abilità di 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. Da adesso lavoreremo sul nostro codice BaseActivitye sui suoi frammenti

  4. Crea un ascoltatore privato in cima alla tua classe BaseActivity

    protected OnBackPressedListener onBackPressedListener;
  5. creare un metodo per impostare il listener BaseActivity

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }
  6. in override onBackPressedimplementare qualcosa del genere

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
  7. nel tuo frammento in onCreateViewte dovresti aggiungere il nostro ascoltatore

    @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, ora quando fai clic indietro nel frammento dovresti prendere il tuo metodo personalizzato sul retro.


Se più di un frammento è un ascoltatore, come si determina, in onBackPressed()quale frammento veniva visualizzato quando è stato premuto il pulsante Indietro?
Al Lelopath,

2
puoi personalizzare il click listener invece il nuovo baseBackPressedListener e chiamarlo lì come anonimo per impostare il proprio comportamento, qualcosa del genere:((BaseActivity)activity).setOnBackPressedListener(new OnBackpressedListener(){ public void doBack() { //...your stuff here }});
pesce morto

Grazie, ho cambiato la mia posizione su questo, tuttavia mi sento di disaccoppiare con i messaggi di trasmissione locali ora. Penso che produrrebbe componenti di qualità superiore che sono altamente disaccoppiati.
HaMMeReD


Questo ha funzionato perfettamente per me, ho riavviato l'attività
raphaelbgr

86

Questo ha funzionato per me: 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
Ha funzionato perfettamente! Ma poi devi anche gestire l'azione predefinita, scrivendo probabilmente un'altra condizione IF. Da allora, stavo abbassando il mio slideUpPanel se fosse espanso. Quindi, prima dovevo contrassegnare un segno di spunta se (pannello è attivo) quindi .....
sud007

18
Il problema con questa soluzione è che getView () restituisce solo la vista principale (e non la vista secondaria, se ottengono lo stato attivo). Quindi, se si dispone di un EditText e si digita del testo, quindi premere una volta "indietro" per nascondere la tastiera, la seconda pressione su "indietro" viene avviata da "Modifica testo" (una vista stessa) e questo metodo non sembra catturare il " indietro ", premere quindi (poiché sta solo catturando per la vista principale). A quanto ho capito, potresti catturare gli eventi di pressione dei tasti sul tuo "EditText" così come qualsiasi altra vista, ma se hai un modulo "complesso", questo non è così pulito e gestibile come potrebbe essere.
Pelpotronic,

Questa è un'ottima soluzione semplice per l'incubo che è frammenti. Se hai un modulo edittext "complesso", probabilmente dovresti eliminare il tuo progetto e ricominciare da capo. Restituisce false quando si desidera utilizzare il comportamento standard. Ritorna vero quando hai intercettato la stampa posteriore e hai fatto qualcosa con esso
behelit

65

Se si desidera quel tipo di funzionalità, è necessario sostituirlo nella propria attività, quindi aggiungere un YourBackPressed un'interfaccia a tutti i frammenti, che si chiama sul frammento pertinente ogni volta che si preme il pulsante Indietro.

Modifica: vorrei aggiungere la mia risposta precedente.

Se dovessi farlo oggi, utilizzerei una trasmissione, o possibilmente una trasmissione ordinata se mi aspettassi che altri pannelli si aggiornassero all'unisono con il pannello principale / principale.

LocalBroadcastManagernella Libreria di supporto può esserti utile e basta inviare la trasmissione onBackPressede iscriverti ai frammenti che ti interessano. Penso che la messaggistica sia un'implementazione più disaccoppiata e si ridimensionerebbe meglio, quindi ora sarebbe la mia raccomandazione ufficiale di implementazione. Basta usare ilIntent 'azione come filtro per il tuo messaggio. invia il tuo nuovo creato ACTION_BACK_PRESSED, invialo dalla tua attività e ascoltalo nei relativi frammenti.


4
Un'idea piuttosto ingegnosa! alcuni pensieri aggiuntivi: + è importante realizzare questa logica con la trasmissione che va da attività> frammenti (non viceversa). OnBackPressed è disponibile solo a livello di attività, quindi intrappolare l'evento lì e trasmetterlo a tutti i frammenti di "ascolto" è la chiave qui. + L'uso di un EventBus (come Otto di Square) rende banale l'implementazione di un caso come questo!
Kaushik Gopal,

2
Non ho ancora usato EventBus di Square, ma sarò in prodotti futuri. Penso che sia una soluzione pure-java migliore, e se riesci a eliminare Android sezioni della tua architettura, tanto meglio.
HaMMeReD

Come si lascia che solo i frammenti migliori gestiscano l'evento? per esempio, vorrai distruggere il frammento attualmente in mostra
eugene,

Il ricevitore di trasmissione dovrebbe essere legato al ciclo di vita se lo stai usando, quindi quando non è visibile non dovrebbe ricevere l'evento.
HaMMeReD,

Si noti inoltre che LocalBroadcastManagernon è possibile effettuare trasmissioni ordinate
Xiao

46

Niente di tutto ciò è facile da implementare né funzionerà in modo ottimale.

I frammenti hanno un metodo chiamato onDetach che farà il lavoro.

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

Questo farà il lavoro.


20
ma il problema qui è che non sai se il frammento è stato staccato a causa di una back press o a causa di qualche altra azione, che ha portato l'utente a passare così qualche altra attività o frammento.
Sunny

7
onDetach () viene chiamato quando il frammento non è più associato alla sua attività. Questo si chiama dopo onDestroy (). Anche se si ottiene questo codice quando si preme il pulsante Indietro, si ottiene anche questo codice quando si cambia da frammento a frammento. Potresti fare qualcosa di intelligente per farlo funzionare ...
apmartin1991,

Funziona bene per un DialogFragment.
CoolMind,

2
Non dimenticare di aggiungere isRemoving()come descritto in stackoverflow.com/a/27103891/2914140 .
CoolMind

1
questo è sbagliato. Può essere chiamato per diversi motivi
Bali,

40

Se stai usando androidx.appcompat:appcompat:1.1.0o sopra, puoi aggiungere un OnBackPressedCallbackframmento come segue

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

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


Se si utilizza androidx-core-ktx, è possibile utilizzarerequireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { /* code to be executed when back is pressed */ }
Max

È importante notare che il LifecycleOwnerparametro deve essere aggiunto come in questo esempio. Senza di essa, eventuali frammenti avviati successivamente chiameranno handleBackPressed()se viene premuto il pulsante Indietro.
Eric B.

questo metodo è necessario con la libreria di navigazione?
Silenzio

25

Basta aggiungere addToBackStackmentre si sta effettuando la transizione tra i frammenti come di seguito:

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

se scrivi addToBackStack(null), lo gestirà da solo ma se dai un tag, dovresti gestirlo manualmente.


C'è qualcos'altro da fare o è sufficiente aggiungere il metodo addToBackStack?
Sreecharan Desabattula,

1
se aggiungi semplicemente che dovrebbe funzionare, se ancora non funziona ci dovrebbe essere qualcosa nel tuo codice. Lascia il codice da qualche parte per vedere per favore @SreecharanDesabattula
Rudi


20

poiché questa domanda e alcune delle risposte hanno più di cinque anni, vorrei condividere la mia soluzione. Questo è un seguito e una modernizzazione della risposta di @oyenigun

AGGIORNARE: Alla fine di questo articolo, ho aggiunto un'implementazione alternativa usando un'estensione astratta del frammento che non coinvolgerà affatto l'attività, che sarebbe utile per chiunque abbia una gerarchia di frammenti più complessa che coinvolge frammenti nidificati che richiedono un diverso comportamento alla schiena.

Avevo bisogno di implementarlo perché alcuni dei frammenti che utilizzo hanno viste più piccole che vorrei eliminare con il pulsante Indietro, come piccole viste di informazioni che compaiono, ecc., Ma questo è utile per chiunque abbia bisogno di ignorare il comportamento di il pulsante indietro all'interno di frammenti.

Innanzitutto, definire un'interfaccia

public interface Backable {
    boolean onBackPressed();
}

Questa interfaccia, che io chiamo Backable(sono un pignolo per le convenzioni di denominazione), ha un solo metodo onBackPressed()che deve restituire un booleanvalore. Dobbiamo imporre un valore booleano perché dovremo sapere se la pressione del pulsante Indietro ha "assorbito" l'evento back. Restituire truesignifica che lo è, e non sono necessarie ulteriori azioni, altrimenti, falseafferma che l'azione di ritorno predefinita deve ancora aver luogo. Questa interfaccia dovrebbe essere il proprio file (preferibilmente in un pacchetto separato chiamato interfaces). Ricorda, separare le tue classi in pacchetti è una buona pratica.

Secondo, trova il frammento superiore

Ho creato un metodo che restituisce l'ultimo Fragment oggetto nello stack posteriore. Uso i tag ... se usi gli ID, apporto le modifiche necessarie. Ho questo metodo statico in una classe di utilità che si occupa di stati di navigazione, ecc ... ma, naturalmente, lo metto dove ti si addice meglio. Per l'edificazione, ho messo il mio in una classe chiamata 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;
}

Assicurarsi che il conteggio dello stack posteriore sia maggiore di 0, altrimenti ArrayOutOfBoundsExceptionpotrebbe essere lanciato in fase di esecuzione. Se non è maggiore di 0, restituisce null. Verificheremo un valore null più tardi ...

In terzo luogo, implementare in un frammento

Implementare l' Backableinterfaccia in qualunque frammento in cui è necessario sovrascrivere il comportamento del pulsante Indietro. Aggiungi il metodo di implementazione.

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

...

    @Override
    public boolean onBackPressed() {

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

}

Nella onBackPressed()sostituzione, inserisci la logica di cui hai bisogno. Se si desidera che il pulsante Indietro non visualizzi lo stack posteriore (comportamento predefinito), tornare true che l'evento back è stato assorbito. Altrimenti, restituisci false.

Infine, nella tua attività ...

Sostituisci il onBackPressed()metodo e aggiungi questa logica ad esso:

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

Otteniamo il frammento corrente nello stack posteriore, quindi eseguiamo un controllo nullo e determiniamo se implementa la nostra Backableinterfaccia. In tal caso, determinare se l'evento è stato assorbito. In tal caso, abbiamo terminato onBackPressed()e possiamo tornare. Altrimenti, trattalo come una normale back press e chiama il metodo super.

Seconda opzione per non coinvolgere l'attività

A volte, non vuoi che l'Attività gestisca questo e devi gestirla direttamente all'interno del frammento. Ma chi dice che non puoi avere frammenti con un'API di back press? Estendi il tuo frammento a una nuova classe.

Crea una classe astratta che estende il frammento e implementa l' View.OnKeyListnerinterfaccia ...

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

Come puoi vedere, qualsiasi frammento che si estende BackableFragmentacquisirà automaticamente i clic indietro utilizzando l' View.OnKeyListenerinterfaccia. Basta chiamare il onBackButtonPressed()metodo astratto dall'interno del onKey()metodo implementato utilizzando la logica standard per discernere una pressione del pulsante Indietro. Se devi registrare i clic sui tasti oltre al pulsante Indietro, assicurati di chiamare il supermetodo quando esegui l'override onKey()nel frammento, altrimenti eseguirai l' override il comportamento nell'astrazione.

Semplice da usare, basta estendere e implementare:

public class FragmentChannels extends BackableFragment {

    ...

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

    ...
}

Poiché il onBackButtonPressed()metodo nella superclasse è astratto, una volta esteso è necessario implementarlo onBackButtonPressed(). Ritorna voidperché deve solo eseguire un'azione all'interno della classe di frammenti e non deve ritrasmettere l'Assorbimento della pressa all'attività. Assicurati di chiamare il onBackPressed()metodo Activity se qualsiasi cosa tu stia facendo con il pulsante Indietro non richiede gestione, altrimenti il ​​pulsante Indietro sarà disabilitato ... e non lo vuoi!

Avvertenze Come puoi vedere, questo imposta l'ascoltatore chiave sulla vista radice del frammento e dovremo focalizzarlo. Se ci sono testi di modifica coinvolti (o qualsiasi altra vista che ruba il focus) nel tuo frammento che estende questa classe (o altri frammenti o viste interni che hanno lo stesso), dovrai gestirlo separatamente. C'è un buon articolo sull'estensione di un EditText per perdere la concentrazione su una stampa precedente.

Spero che qualcuno lo trovi utile. Buona codifica.


Grazie, è una bella soluzione. Puoi dire, perché chiami super.onBackPressed();due volte?
CoolMind

Viene chiamato solo una volta per scenario per quanto riguarda lo stato null di currentFragment . Se il frammento non è nullo e il frammento implementa l' Backableinterfaccia, non viene intrapresa alcuna azione. Se il frammento non è nullo e NON implementa Backable, chiamiamo il metodo super. Se il frammento è nullo, salta all'ultima super chiamata.
mwieczorek,

Scusa, non ho capito. Nel caso in cui acurrentFragment non sia nullo ed è un'istanza di Backable ed è staccato (abbiamo premuto il backpulsante e abbiamo chiuso il frammento) super.onBackPressed();viene chiamata la prima occorrenza di , quindi la seconda.
CoolMind,

Ottima soluzione
David Toledo,

Forse "Closeable" suona un po 'meglio di "Backable"
pumnao

15

La soluzione è semplice:

1) Se hai una classe di frammenti di base estesa da tutti i frammenti, aggiungi questo codice alla sua classe, altrimenti crea una tale classe di frammenti di base

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

2) Nella tua classe Activity, sovrascrivi onBackPress come segue:

private BaseFragment _currentFragment;

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

3) Nella classe Il tuo frammento, aggiungi il codice desiderato:

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


9

onBackPressed() fa staccare il frammento dall'attività.

Secondo la risposta di @Sterling Diaz penso che abbia ragione. MA qualche situazione sarà sbagliata. (es. Ruota schermo)

Quindi, penso che potremmo rilevare se isRemoving() raggiungere gli obiettivi.

Puoi scriverlo a onDetach()o onDestroyView(). È lavoro.

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

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

8

Bene, l'ho fatto in questo modo e funziona per me

Interfaccia semplice

FragmentOnBackClickInterface.java

public interface FragmentOnBackClickInterface {
    void onClick();
}

Esempio di implementazione

MyFragment.java

public class MyFragment extends Fragment implements FragmentOnBackClickInterface {

// other stuff

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

quindi sovrascrivi onBackPress in attività

    @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;
}

Non funziona ! super.onbackpressed () viene chiamato sempre :(
Animesh Mangla

Come passare questo evento Inner Fragment. Ho ViewPager in Activity e quel ViewPager ha frammenti, ora tutti quei frammenti hanno un frammento figlio. Come restituire l'evento pulsante a quei frammenti figlio.
Kishan Vaghela,

@KishanVaghela bene da allora non ho più toccato Android ma ho un'idea. Dammi il 24 per aggiornare la mia risposta.
Błażej,

Ok nessun problema, un'opzione è che devo passare l'evento listener a ogni frammento figlio dal frammento genitore. ma non è un modo ideale perché devo farlo per ogni frammento.
Kishan Vaghela,

@KishanVaghela Stavo pensando al manager in attività. È possibile aggiungere / rimuovere frammenti con quell'interfaccia. Ma con questo approccio devi implementare manager in ogni frammento che ha delle subframmentazioni. Quindi forse una soluzione migliore sarà la creazione di manager come singleton. Quindi dovrai aggiungere / rimuovere frammenti / oggetti e in 'onBackPressed' chiami il metodo 'onBackPressed' di questo gestore. Tale metodo chiamerà il metodo "onClick" in ogni oggetto che è stato aggiunto al gestore. Ti è più o meno chiaro?
Błażej,

8

È necessario aggiungere un'interfaccia al progetto come di seguito;

public interface OnBackPressed {

     void onBackPressed();
}

E quindi, dovresti implementare questa interfaccia sul tuo frammento;

public class SampleFragment extends Fragment implements OnBackPressed {

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

}

E puoi attivare questo evento onBackPressed nelle tue attività su evento BackPress come di seguito;

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

È un buon metodo, ma fai attenzione, non chiamare getActivity().onBackPressed();perché invocherà un'eccezione: "java.lang.StackOverflowError: stack size 8MB".
CoolMind

8

Questo è solo un piccolo codice che farà il trucco:

 getActivity().onBackPressed();

Spero che aiuti qualcuno :)


4
Ha chiesto di ignorare il comportamento, non semplicemente di invocare il metodo di attività.
mwieczorek,

2
L'ho letto attentamente, grazie. La tua soluzione inoltra semplicemente il metodo onBackPressed all'attività, quando ha chiesto come eseguire l'override.
mwieczorek,

7

Se usi EventBus, è probabilmente una soluzione molto più semplice:

Nel tuo frammento:

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

e nella tua classe Activity puoi definire:

@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 è solo un oggetto POJO

Questo è super pulito e non ci sono problemi di interfaccia / implementazione.


7

questa è la mia soluzione:

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

e nel frammento:

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

        // some codes

        return true;
    }
});

6

Utilizzando il componente Navigazione puoi farlo in questo modo :

Giava

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

Che ne dici di usare onDestroyView ()?

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

5
che ne dici di passare a un altro frammento dal frammento reale, cosa accadrà usando il tuo metodo? :)
Choletski,

La risposta più inutile. È lo stesso di scrivere i = io 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;
        }
    });
}

Solo se i frammenti non hanno una vista focalizzabile (come modificare il testo), lascia che i frammenti gestiscano il pulsante indietro come fa questa risposta. Altrimenti, lascia che l'attività contenente frammenti lo faccia da OnBackPress () mentre viene scritta la prima risposta; poiché una vista focalizzabile ruberà l'attenzione del frammento. Tuttavia, possiamo riottenere il focus per il frammento focalizzando chiaramente tutte le viste focalizzabili con il metodo clearFocus () e ri-focus sul frammento.
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();
    }
}

nel tuo frammento aggiungi quanto segue, non dimenticare di implementare l'interfaccia della mainattività.

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

Nella mia soluzione (Kotlin);

Sto usando la funzione onBackAlternative come parametro su BaseActivity .

BaseActivity

abstract class BaseActivity {

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

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

Ho una funzione per impostare onBackPressAlternative su 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
     }
}

Quindi il mio onBackPressAlternative è pronto per l'uso sui frammenti.

Frammenti secondari

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

3

Non implementare il metodo ft.addToBackStack () in modo che quando si preme il pulsante Indietro la propria attività sarà terminata.

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


3

Segui questi passaggi:

Sempre mentre aggiungi un frammento,

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

Quindi, nell'attività principale, eseguire l'override onBackPressed()

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

Per gestire il pulsante Indietro nella tua app,

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

Questo è tutto!


3

Nel ciclo di vita delle attività, sempre il pulsante Indietro di Android gestisce le transazioni di FragmentManager quando abbiamo usato FragmentActivity o AppCompatActivity.

Per gestire il backstack non abbiamo bisogno di gestire il conteggio del backstack o taggare nulla ma dovremmo rimanere concentrati durante l'aggiunta o la sostituzione di un frammento. Trova i seguenti frammenti per gestire le custodie dei pulsanti Indietro,

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

Qui, non aggiungerò back stack per il mio frammento home perché è la home page della mia applicazione. Se aggiungi addToBackStack a HomeFragment, l'app aspetterà di rimuovere tutti i frammenti di attività, quindi avremo uno schermo vuoto quindi mantengo le seguenti condizioni,

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

Ora puoi vedere il frammento precedentemente aggiunto su acitvity e l'app verrà chiusa quando raggiungi HomeFragment. puoi anche guardare i seguenti frammenti.

@Override
public void onBackPressed() {

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

3

Frammento: crea un BaseFragment posizionando un metodo:

 public boolean onBackPressed();

Attività:

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

La tua attività passerà sopra i frammenti collegati e visibili e chiamerà onBackPressed () su ciascuno di essi e si interromperà se uno di essi restituisce "vero" (il che significa che è stato gestito, quindi nessuna ulteriore azione).


Signore, questo è il modo più elegante e funziona perfettamente!
Asozcan,

3

Secondo le note di rilascio di AndroidX , androidx.activity 1.0.0-alpha01viene rilasciato e introduce ComponentActivityuna nuova classe base di quelli esistenti FragmentActivitye AppCompatActivity. E questa versione ci offre una nuova funzionalità:

Ora puoi registrare un OnBackPressedCallbackvia addOnBackPressedCallbackper ricevere i onBackPressed()callback senza dover sovrascrivere il metodo nella tua attività.


Puoi mostrare un esempio di questo? Non riesco a risolvere quel metodo.
Bryan Bryce,

1
@BryanBryce Non ho trovato un esempio ma il documento ufficiale: developer.android.com/reference/androidx/activity/…
TonnyL

Il metodo non risolve b / c AppCompatActivity si sta attualmente estendendo da androidx.core.app.ComponentActivity invece di androidx.activity.ComponentActivity.
Wooldridgetm,

3

È possibile registrare il frammento nell'attività per gestire il back press:

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

interface BackPressHandler {
    fun onBackPressed(): Boolean
}

utilizzo:

Nel frammento:

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 attività:

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

Fornire la navigazione posteriore personalizzata gestendo onBackPressed è ora più facile con i callback all'interno del frammento.

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
    )
    ...
}

Se si desidera l'azione posteriore predefinita in base a una condizione, è possibile utilizzare:

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

3

All'interno del metodo onCreate del frammento aggiungere quanto segue:

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

dopo aver aggiunto questo backpress myFragment funziona ma ora l'attività backpress non ha funzionato
Sunil Chaudhary

2

Soluzione migliore,

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

            }
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.