Fragment onResume () & onPause () wird im Backstack nicht aufgerufen

194

Ich habe mehrere Fragmente in einer Aktivität. Bei einem Klick auf eine Schaltfläche starte ich ein neues Fragment und füge es dem Backstack hinzu. Ich habe natürlich erwartet, dass die onPause()Methode des aktuellen Fragments und onResume()des neuen Fragments aufgerufen wird. Nun, es passiert nicht.

LoginFragment.java

public class LoginFragment extends Fragment{
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      final View view  =   inflater.inflate(R.layout.login_fragment, container, false);
      final FragmentManager mFragmentmanager =  getFragmentManager();

      Button btnHome  = (Button)view.findViewById(R.id.home_btn);
      btnHome.setOnClickListener(new View.OnClickListener() {
        public void onClick(View view){
           HomeFragment fragment    = new HomeFragment();
           FragmentTransaction ft2   =  mFragmentmanager.beginTransaction();
           ft2.setCustomAnimations(R.anim.slide_right, R.anim.slide_out_left
                    , R.anim.slide_left, R.anim.slide_out_right);
           ft2.replace(R.id.middle_fragment, fragment);
           ft2.addToBackStack(""); 
           ft2.commit();    
         }
      });
  }

  @Override
  public void onResume() {
     Log.e("DEBUG", "onResume of LoginFragment");
     super.onResume();
  }

  @Override
  public void onPause() {
    Log.e("DEBUG", "OnPause of loginFragment");
    super.onPause();
  }
}

HomeFragment.java

public class HomeFragment extends Fragment{
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
     final View view  =   inflater.inflate(R.layout.login_fragment, container, false);
  }

  @Override
  public void onResume() {
     Log.e("DEBUG", "onResume of HomeFragment");
     super.onResume();
  }

  @Override
  public void onPause() {
     Log.e("DEBUG", "OnPause of HomeFragment");
     super.onPause();
  }
}

Was ich erwartet hatte, war:

  1. Wenn der Knopf angeklickt wird, LoginFragment wird mit ersetzt HomeFragment , onPause()von LoginFragment und onResume()von HomeFragment wird aufgerufen
  2. Bei der Wiedergabe gedrückt wird, HomeFragment wird poped und LoginFragment gesehen wird , und onPause()von HomeFragment und onResume()von LoginFragment aufgerufen wird.

Was ich bekomme ist,

  1. Wenn Sie auf die Schaltfläche klicken , ersetzt HomeFragment LoginFragment korrekt . OnResume () von HomeFragment wird aufgerufen, onPause () von LoginFragment wird jedoch nie aufgerufen.
  2. Wenn Sie zurück drücken, wird HomeFragment korrekt angezeigt , um LoginFragment anzuzeigen . OnPause () von HomeFragment wird aufgerufen, onResume () von LoginFragment wird jedoch nie aufgerufen.

Ist das das normale Verhalten? Warum ist onResume()das LoginFragment nicht genannt wird , wenn ich die Zurück - Taste drücken.

Krishnabhadra
quelle
Fügen Sie den Aktivitätscode hinzu, der die Fragmente verarbeitet.
Blessenm
Ich habe das Beispielproblem, in der Pause nicht angerufen, wie haben Sie das gelöst,
Sam
Ich hatte das gleiche Problem, stellte jedoch fest, dass ich ft2.add () verwendete. anstelle von ft2.replace (). Der einzige andere Grund wäre, wenn Ihre Aktivität einen Verweis auf das Fragment behält (Hinzufügen zu einer Sammlung oder Zuweisen zu einer Klassenvariablen)
Friggles
3
Ich habe das gleiche Problem. Ich habe festgestellt, dass .replace () die erforderlichen Lebenszyklusmethoden aufruft, das Fragment jedoch im Wesentlichen zerstört. Außerdem wird onSaveInstanceState nicht aufgerufen. Als solches kann ich seinen Zustand nicht beibehalten. Also muss ich add verwenden, aber die onResume / Pause heißt nicht :(
ariets
FWIW, meine Erfahrung ist, dass Unterstützungsbibliotheksfragmente beim Push / Popping-Backstack onPause und onResume aufrufen, die in Android integrierten Fragmente jedoch nicht. Ich habe noch keine richtige Lösung dafür gefunden.
Benkc

Antworten:

185

Die Fragmente onResume()oder onPause()werden nur aufgerufen, wenn die Aktivitäten onResume()oder onPause()aufgerufen werden. Sie sind eng mit dem verbundenActivity .

Lesen Sie den Abschnitt Umgang mit dem Fragmentlebenszyklus dieses Artikels .

Sagar Waghmare
quelle
1
In dem Artikel heißt es: "Sobald die Aktivität den wiederaufgenommenen Zustand erreicht hat, können Sie der Aktivität frei Fragmente hinzufügen und daraus entfernen. Nur wenn sich die Aktivität im wiederaufgenommenen Zustand befindet, kann sich der Lebenszyklus eines Fragments unabhängig ändern." Bedeutet dies, dass das Fragment onResume aufgerufen werden kann, auch wenn die Aktivität onResume nicht aufgerufen wird?
v4r
3
Bei nativen (nicht unterstützten) Fragmenten in 4.4 (nicht sicher, ob dies für ältere Versionen zutrifft) werden onPause () und onResume () nicht nur aufgerufen, wenn diese Ereignisse in der Aktivität auftreten, sondern beispielsweise, wenn Sie replace () oder add aufrufen () / remove () während der Ausführung der Transaktion, daher ist diese Antwort zumindest für die neuesten Versionen von Android irreführend.
Dmide
19
Gemäß diesem Dokument sollte das Fragment tatsächlich in den gestoppten Zustand versetzt werden, wenn es in den Backstack ausgelagert wird. Aber nicht nur onPause und onResume werden nicht aufgerufen, auch nicht onStop und onStart - oder andere Lebenszyklusmethoden. Der Leitfaden ist also definitiv irreführend.
Benkc
1
Obwohl nicht verwandt, aber ich fand diese Frage bei der Suche nach meinem Problem onPause(), nachher gerufen zu werden, onSaveInstanceState()anstatt davor. Dies kann reproduziert werden, wenn Sie zu einem anderen Kind in meinem FragmentStatePagerAdapter(sagen Sie, Sie wechseln von Kind 0 zu Kind 2, beachten Sie zu sich selbst, dies geschieht, weil Kind 0 zerstört wird, wenn Kind 2 öffnet )
Sufian
Nicht sicher was du meinst. Das Aufrufen von ft.replace sollte die onPause (des ersetzten Fragments) und onResume (des ersetzenden Fragments) auslösen. Dies geschieht unabhängig von Aktivitäten ...
David Refaeli
20
  • Da Sie verwendet haben ft2.replace(), wird die FragmentTransaction.remove() Methode aufgerufen und die Loginfragmentwird entfernt. Beziehen Sie sich darauf . Also onStop()von LoginFragmentwird statt genanntonPause() . (Da das neue Fragment das alte vollständig ersetzt).
  • Aber da Sie auch verwendet haben ft2.addtobackstack(), der Zustand der Loginfragmentwird als ein Bündel gespeichert werden und wenn Sie auf Back - Button aus HomeFragment, onViewStateRestored()wird anschließend aufgerufen wird onStart()von LoginFragment. Also wird irgendwann onResume()nicht mehr angerufen.
Gopal
quelle
1
onViewStateRestoredwird angerufen, wenn Sie habensetRetainInstance(true)
Farid
14

Hier ist meine robustere Version von Gors Antwort (die Verwendung von fragment.size () ist unzuverlässig, da die Größe nach dem Aufspringen des Fragments nicht dekrementiert wird.)

getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            if (getFragmentManager() != null) {

                Fragment topFrag = NavigationHelper.getCurrentTopFragment(getFragmentManager());

                if (topFrag != null) {
                    if (topFrag instanceof YourFragment) {
                        //This fragment is being shown. 
                    } else {
                        //Navigating away from this fragment. 
                    }
                }
            }
        }
    });

Und die Methode 'getCurrentTopFragment':

public static Fragment getCurrentTopFragment(FragmentManager fm) {
    int stackCount = fm.getBackStackEntryCount();

    if (stackCount > 0) {
        FragmentManager.BackStackEntry backEntry = fm.getBackStackEntryAt(stackCount-1);
        return  fm.findFragmentByTag(backEntry.getName());
    } else {
        List<Fragment> fragments = fm.getFragments();
        if (fragments != null && fragments.size()>0) {
            for (Fragment f: fragments) {
                if (f != null && !f.isHidden()) {
                    return f;
                }
            }
        }
    }
    return null;
}
aaronmarino
quelle
9

Wenn Sie das Fragment wirklich durch ein anderes Fragment ersetzen möchten, sollten Sie verschachtelte Fragmente verwenden .

In Ihrem Code sollten Sie ersetzen

final FragmentManager mFragmentmanager =  getFragmentManager();

mit

final FragmentManager mFragmentmanager =  getChildFragmentManager();
Bersh
quelle
5
getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            List<Fragment> fragments = getFragmentManager().getFragments();
            if (fragments.size() > 0 && fragments.get(fragments.size() - 1) instanceof YoureFragment){
                //todo if fragment visible
            } else {
                //todo if fragment invisible
            }

        }
    });

Seien Sie jedoch vorsichtig, wenn mehr als ein Fragment sichtbar ist

Gor
quelle
Danke, es funktioniert, aber wenn nur ein Fragment sichtbar ist (also ViewPagerverweisen Fragmente auf seine Fragmente).
CoolMind
3

Ich habe einen Code, der Ihrem sehr ähnlich ist und ob er auf pause () und onResume () funktioniert. Beim Ändern des Fragments werden diese Funktionen jeweils aktiviert.

Code in Fragment:

 @Override
public void onResume() {
    super.onResume();
    sensorManager.registerListener(this, proximidad, SensorManager.SENSOR_DELAY_NORMAL);
    sensorManager.registerListener(this, brillo, SensorManager.SENSOR_DELAY_NORMAL);
    Log.e("Frontales","resume");
}

@Override
public void onPause() {
    super.onPause();
    sensorManager.unregisterListener(this);
    Log.e("Frontales","Pause");

}

Protokoll beim Fragmentwechsel:

05-19 22:28:54.284 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:28:57.002 2371-2371/madi.cajaherramientas E/Frontales: Pause
05-19 22:28:58.697 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:29:00.840 2371-2371/madi.cajaherramientas E/Frontales: Pause
05-19 22:29:02.248 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:29:03.718 2371-2371/madi.cajaherramientas E/Frontales: Pause

Fragment onCreateView:

View rootView;
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    rootView = inflater.inflate(R.layout.activity_proximidad, container, false);
    ButterKnife.bind(this,rootView);
    inflar();
    setTextos();
    return rootView;
}

Aktion beim Zurückpulsieren (in der Aktivität, in der ich das Fragment lade):

@Override
public void onBackPressed() {

    int count = getFragmentManager().getBackStackEntryCount();

    if (count == 0) {
        super.onBackPressed();

    } else {
        getFragmentManager().popBackStack();
    }

 }
PepitoGrillo
quelle
2

Was ich im Kinderfragment mache:

@Override
public void onDetach() {
   super.onDetach();
   ParentFragment pf = (ParentFragment) this.getParentFragment();
   pf.onResume();
}

Und überschreiben Sie dann onResume on ParentFragment

Guillermo De Juan
quelle
1
Sie sollten Lebenszyklusmethoden niemals manuell aufrufen, insbesondere nicht ineinander
Breakline
@breakline diese Technik funktioniert. Hast du einen anderen Weg?
Vikash Parajuli
Ja, Sie sollten Ihre eigene Implementierung zum Aufrufen hinzufügen, da Lebenszyklusmethoden auch vom System aufgerufen werden. Wenn Sie Lebenszyklusmethoden wie folgt ineinander aufrufen, können (und werden höchstwahrscheinlich) spätere Probleme auftreten.
Bruchlinie
1

Sie können einem Fragment einfach kein Fragment hinzufügen. Dies muss in der FragmentActivity geschehen. Ich gehe davon aus, dass Sie das LoginFragment in einer FragmentActivity erstellen. Damit dies funktioniert, müssen Sie das HomeFragment über die FragmentActivity hinzufügen, wenn die Anmeldung geschlossen wird.

Der allgemeine Punkt ist, dass Sie eine FragmentActivity-Klasse benötigen, von der aus Sie jedes Fragment zum FragmentManager hinzufügen. Es ist nicht möglich , diese in einem Fragmente Klasse zu tun.

Nickolaus
quelle
Ja, sie befinden sich in einer Fragmentaktivität.
Krishnabhadra
Wenn Fragmente dynamisch hinzugefügt werden, können Sie einem Fragment so viele Fragmente hinzufügen, wie Sie möchten, jedoch nicht zu den im xml <fragment> -Tag definierten Fragmenten
Illegales Argument
1

Wenn Sie das Fragment in XML hinzufügen, können Sie es nicht dynamisch austauschen. Was passiert, ist, dass sie übermäßig sind, sodass die Ereignisse nicht wie erwartet ausgelöst werden. Das Problem ist in dieser Frage dokumentiert.FragmenManager ersetzen macht Overlay

Verwandeln Sie middle_fragment in ein FrameLayout und laden Sie es wie unten beschrieben. Ihre Ereignisse werden ausgelöst.

getFragmentManager().beginTransation().
    add(R.id.middle_fragment, new MiddleFragment()).commit();
Dustin
quelle
1

Sie können dies versuchen,

Schritt 1: Überschreiben Sie die Tabselected-Methode in Ihrer Aktivität

@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    // When the given tab is selected, switch to the corresponding page in
    // the ViewPager.
    try {
    if(MyEventsFragment!=null && tab.getPosition()==3)
    {
        MyEvents.fragmentChanged();
    }
    }
    catch (Exception e)
    {

    }
    mViewPager.setCurrentItem(tab.getPosition());
}

Schritt 2: Verwenden Sie die statische Methode, um das zu tun, was Sie in Ihrem Fragment wollen.

public static void fragmentChanged()
{
    Toast.makeText(actvity, "Fragment Changed", Toast.LENGTH_SHORT).show();
}
Manoj Kumar
quelle
1

Ich benutze in meiner Tätigkeit - KOTLIN

supportFragmentManager.addOnBackStackChangedListener {
                val f = supportFragmentManager.findFragmentById(R.id.fragment_container)

                if (f?.tag == "MyFragment")
                {
                    //doSomething
                }
            }
Álvaro Agüero
quelle
0

Ein Fragment muss immer in eine Aktivität eingebettet sein, und der Lebenszyklus des Fragments wird direkt vom Lebenszyklus der Hostaktivität beeinflusst. Wenn zum Beispiel die Aktivität angehalten wird, sind alle Fragmente darin, und wenn die Aktivität zerstört wird, sind auch alle Fragmente darin

Zar E Ahmer
quelle
0

onPause() Methode funktioniert in Aktivitätsklasse, die Sie verwenden können:

public void onDestroyView(){
super.onDestroyView    
}

für den gleichen Zweck ..

Vikrant Kaloniya
quelle
0

Befolgen Sie die folgenden Schritte, und Sie erhalten die erforderliche Antwort

1- Erstellen Sie für beide Fragmente ein neues abstraktes übergeordnetes Fragment.
2- Fügen Sie eine benutzerdefinierte abstrakte Methode hinzu, die von beiden implementiert werden soll.
3- Rufen Sie es von der aktuellen Instanz aus auf, bevor Sie es durch die zweite ersetzen.

Mostafa Magdy
quelle
Wie hilft es in Situationen, in denen ein Benutzer von Fragment 2 zu Fragment 1 zurückkehrt?
CoolMind
0

Basierend auf der Antwort von @Gor habe ich ähnlich in Kotlin geschrieben. Platzieren Sie diesen Code in onCreate()einer Aktivität. Es funktioniert für ein sichtbares Fragment. Wenn Sie ViewPagerFragmente haben, wird ViewPagerdas Fragment aufgerufen , kein vorheriges.

supportFragmentManager.addOnBackStackChangedListener {
    supportFragmentManager.fragments.lastOrNull()?.onResume()
}

Nachdem ich https://medium.com/@elye.project/puzzle-fragment-stack-pop-cause-issue-on-toolbar-8b947c5c07c6 gelesen hatte, wurde mir klar, dass es in vielen Situationen besser wäre, neue Fragmente anzuhängen replace, nicht add. So wird onResumein einigen Fällen ein Bedarf verschwinden.

CoolMind
quelle
0

Berufung ft.replace sollte onPause (des ersetzten Fragments) und onResume (des ersetzenden Fragments) auslösen.

Ich login_fragmentstelle fest, dass sich Ihr Code auf dem Home-Fragment aufbläst und auch die Ansichten in onCreateView nicht zurückgibt. Wenn dies Tippfehler sind, können Sie zeigen, wie diese Fragmente aus Ihrer Aktivität heraus aufgerufen werden?

David Refaeli
quelle
Dies ist keine Antwort. Was ist, wenn "Hinzufügen" ein Muss für das Design von jemandem ist?!
Farid
0

Obwohl mit unterschiedlichem Code, hatte ich das gleiche Problem wie das OP, weil ich es ursprünglich verwendet habe

fm.beginTransaction()
            .add(R.id.fragment_container_main, fragment)
            .addToBackStack(null)
            .commit();

anstatt

fm.beginTransaction()
                .replace(R.id.fragment_container_main, fragment)
                .addToBackStack(null)
                .commit();

Mit "Ersetzen" wird das erste Fragment neu erstellt, wenn Sie vom zweiten Fragment zurückkehren, und daher wird auch onResume () aufgerufen.

Greg Holst
quelle
Dies ist keine Antwort. Was ist, wenn "Hinzufügen" ein Muss für das Design von jemandem ist?!
Farid
-2

Stellen Sie beim Erstellen einer Fragmenttransaktion sicher, dass Sie den folgenden Code hinzufügen.

// Replace whatever is in the fragment_container view with this fragment, 
// and add the transaction to the back stack 
transaction.replace(R.id.fragment_container, newFragment); 
transaction.addToBackStack(null); 

Stellen Sie außerdem sicher, dass Sie die Transaktion festschreiben, nachdem Sie sie zum Backstack hinzugefügt haben

Mayur Mehr
quelle