Fragment onResume () i onPause () nie jest wywoływany przy backstacku


195

Mam wiele fragmentów wewnątrz działania. Po kliknięciu przycisku zaczynam nowy fragment, dodając go do backstacka. Naturalnie spodziewałem się, że zostanie wywołana onPause()metoda obecnego Fragmentu i onResume()nowego Fragmentu. Cóż, to się nie dzieje.

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

Spodziewałem się

  1. Po kliknięciu przycisku LoginFragment zostaje zastąpiony HomeFragment , onPause()z LoginFragment , a onResume()od HomeFragment jest wywoływana
  2. Po powrocie powoduje HomeFragment jest poped się i LoginFragment widać, a onPause()od HomeFragment i onResume()od LoginFragment zostanie wywołany.

Dostaję to

  1. Po kliknięciu przycisku HomeFragment poprawnie zastępuje LoginFragment , wywoływana jest funkcja onResume () funkcji HomeFragment , ale funkcja onPause () funkcji LoginFragment nigdy nie jest wywoływana.
  2. Po ponownym naciśnięciu HomeFragment wyświetla się poprawnie, aby pokazać LoginFragment , onPause () z HomeFragment zostaje wywołany, ale onResume () z LoginFragment nigdy nie zostaje wywołany.

Czy to jest normalne zachowanie? Dlaczego onResume()od LoginFragment nie uzyskiwanie nazywa się po naciśnięciu przycisku Wstecz.


Dodaj kod aktywności, który obsługuje fragmenty.
blessenm

mam przykładowy problem, w przypadku pauzy nie zostaję wezwany, jak to rozwiązałeś,
Sam

Miałem ten sam problem, ale zdałem sobie sprawę, że korzystam z ft2.add (); zamiast ft2.replace (). Jedynym innym powodem byłoby, gdyby Twoja aktywność zachowywała odniesienie do fragmentu (dodając go do kolekcji lub przypisując do zmiennej klasy)
Friggles

3
Mam ten sam problem. Zauważyłem, że .replace () wywoła niezbędne metody cyklu życia, ale zasadniczo niszczy fragment. Również onSaveInstanceState nie jest wywoływany. Jako taki nie mogę utrzymać stanu. Tak więc muszę użyć add, ale onResume / Pause nie jest nazywany :(
ariets

FWIW, z mojego doświadczenia wynika, że ​​fragmenty bibliotek obsługujących wywołują funkcje Wstrzymaj i Włącz ponownie podczas wypychania / wyskakiwania z powrotem, ale wbudowane fragmenty Androida tego nie robią. Nie znalazłem jeszcze odpowiedniego obejścia tego problemu.
benkc

Odpowiedzi:


185

Fragmenty onResume()lub onPause()będą wywoływane tylko wtedy, gdy zostaną wywołane Działania onResume()lub onPause(). Są ściśle powiązane z Activity.

Przeczytaj sekcję Obsługa cyklu życia fragmentu tego artykułu .


1
W artykule jest powiedziane, że „gdy aktywność osiągnie stan wznowienia, możesz dowolnie dodawać i usuwać fragmenty działania. Zatem tylko wtedy, gdy działanie jest w stanie wznowienia, cykl życia fragmentu może się zmieniać niezależnie”. Czy to oznacza, że ​​fragment onResume można wywołać, nawet jeśli aktywność onResume nie jest wywoływana?
v4r

3
jak dla natywnych (nieobsługiwanych) fragmentów w 4.4 (nie jestem pewien, czy to prawda dla starszych wersji) onPause () i onResume () są wywoływane nie tylko, gdy te zdarzenia występują w działaniu, ale na przykład, gdy wywołujesz replace () lub dodajesz () / remove () podczas przeprowadzania transakcji, więc ta odpowiedź wprowadza w błąd przynajmniej w przypadku najnowszych wersji Androida.
Dmide

19
Zgodnie z tym dokumentem fragment powinien zostać faktycznie przeniesiony do stanu zatrzymania po zamianie w backstack. Ale nie tylko onPause i onResume nie są wywoływane, ani onStop i onStart - ani, w tym przypadku, żadne inne metody cyklu życia. Tak więc przewodnik zdecydowanie wprowadza w błąd.
benkc

1
Chociaż nie jest spokrewniony, ale znalazłem to pytanie, szukając problemu z onPause()wywołaniem onSaveInstanceState()zamiast przed nim. To może być powielana jeśli was do innego dziecka w moim FragmentStatePagerAdapter(mówią, że przejście od dziecka do dziecka 2: 0, notatki do siebie, tak się dzieje, ponieważ dziecko 0 jest zniszczona, gdy dziecko 2 otwiera )
Sufian

Nie wiem co masz na myśli. Wywołanie ft.replace powinno uruchomić onPause (zastępowanego fragmentu) i onResume (zastępującego fragmentu). Odbywa się to niezależnie od jakiejkolwiek działalności ...
David Refaeli,

20
  • Ponieważ użyłeś ft2.replace(), FragmentTransaction.remove() wywoływana jest metoda, a Loginfragmentzostanie usunięty. Zobacz to . Więc onStop()od LoginFragmentbędziemy nazywać zamiast onPause(). (Ponieważ nowy fragment całkowicie zastępuje stary).
  • Ale skoro masz również używany ft2.addtobackstack(), stanu Loginfragmentzostanie zapisany jako pakiet i po kliknięciu przycisku z tyłu HomeFragment, onViewStateRestored()będzie zwany następnie onStart()od LoginFragment. Więc w końcu onResume()nie zostanie nazwany.

1
onViewStateRestorednazywa się, jeśli maszsetRetainInstance(true)
Farid

14

Oto moja bardziej niezawodna wersja odpowiedzi Gor (użycie fragments.size () jest niewiarygodne, ponieważ rozmiar nie jest zmniejszany po wysunięciu fragmentu)

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

I metoda „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;
}

9

Jeśli naprawdę chcesz zastąpić fragment w innym fragmencie, powinieneś użyć zagnieżdżonych fragmentów .

W swoim kodzie powinieneś zastąpić

final FragmentManager mFragmentmanager =  getFragmentManager();

z

final FragmentManager mFragmentmanager =  getChildFragmentManager();

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
            }

        }
    });

ale bądź ostrożny, jeśli widoczny jest więcej niż jeden fragment


Dzięki, to działa, ale jeśli widoczny jest tylko jeden fragment (więc ViewPagerprzy fragmentach będzie się odnosił do jego fragmentów).
CoolMind,

3

Mam kod bardzo podobny do twojego i jeśli działa onPause () i onResume (). Podczas zmiany fragmentu funkcje te są odpowiednio aktywowane.

Kod we fragmencie:

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

}

Zaloguj się, gdy zmiana fragmentu:

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

Akcja po odesłaniu pulsu (w działaniu, w którym ładuję fragment):

@Override
public void onBackPressed() {

    int count = getFragmentManager().getBackStackEntryCount();

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

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

 }

2

Co robię we fragmencie podrzędnym:

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

A następnie zastąp overResume na ParentFragment


1
Nigdy nie powinieneś ręcznie wywoływać metod cyklu życia, szczególnie wewnątrz siebie
przerwa

@breakline ta technika działa. Czy masz jakiś inny sposób?
Vikash Parajuli

Tak, powinieneś dodać własną implementację do wywołania, ponieważ metody cyklu życia są również wywoływane przez system, a jeśli wywołasz metody metod cyklu życia w ten sposób, możesz (i najprawdopodobniej) spowodować późniejsze problemy.
breakline

1

Po prostu nie można dodać fragmentu do fragmentu. To musi się zdarzyć w FragmentActivity. Zakładam, że tworzysz LoginFragment w FragmentActivity, więc aby to zadziałało, musisz dodać HomeFragment poprzez FragmentActivity, gdy logowanie zostanie zamknięte.

Ogólnie rzecz biorąc, potrzebujesz klasy FragmentActivity, z której dodajesz każdy fragment do menedżera FragmentManager. Nie można tego zrobić wewnątrz klasy Fragment.


Tak, są częścią działania fragmentu.
Krishnabhadra

jeśli fragmenty są dodawane dynamicznie, możesz dodać tyle fragmentów, ile chcesz do fragmentu, ale nie do tych zdefiniowanych w znaczniku xml <fragment>
Nielegalny argument

1

Jeśli dodasz fragment w XML, nie będziesz mógł zamieniać go dynamicznie. To, co się dzieje, to przesada, więc wydarzenia nie strzelają tak, jak można by się spodziewać. Problem został udokumentowany w tym pytaniu. FragmenManager replace tworzy nakładkę

Przekształć middle_fragment w FrameLayout i załaduj go jak poniżej, a twoje wydarzenia się uruchomią.

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

1

Możesz spróbować

Krok 1: Zastąp metodę Tabselected w swojej aktywności

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

Krok 2: Używając metody statycznej, rób co chcesz w swoim fragmencie,

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

1

Używam w swojej działalności - KOTLIN

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

                if (f?.tag == "MyFragment")
                {
                    //doSomething
                }
            }

0

Fragment musi zawsze być osadzony w działaniu, a na cykl życia fragmentu ma bezpośredni wpływ cykl życia hosta. Na przykład, gdy aktywność jest wstrzymana, podobnie jak wszystkie jej fragmenty, a gdy aktywność jest zniszczona, podobnie jak wszystkie fragmenty


0

onPause() metoda działa w klasie aktywności, której możesz użyć:

public void onDestroyView(){
super.onDestroyView    
}

w tym samym celu ..


0

Wykonaj poniższe kroki, a otrzymasz potrzebną odpowiedź

1- Dla obu fragmentów utwórz nowy abstrakcyjny nadrzędny.
2- Dodaj niestandardową metodę abstrakcyjną, która powinna być zaimplementowana przez obie z nich.
3- Wywołaj go z bieżącej instancji przed zastąpieniem drugą.


Jak to pomaga w sytuacji, gdy użytkownik powraca z fragmentu 2 do fragmentu 1?
CoolMind,

0

Na podstawie odpowiedzi @Gor napisałem podobnie w Kotlinie. Umieść ten kod w onCreate()działaniu. Działa dla jednego widocznego fragmentu. Jeśli masz ViewPagerfragmenty, wywoła ViewPagerfragment, a nie poprzedni.

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

Po przeczytaniu https://medium.com/@elye.project/puzzle-fragment-stack-pop-cause-issue-on-toolbar-8b947c5c07c6 Zrozumiałem, że w wielu sytuacjach lepiej byłoby dołączyć nowe fragmenty replace, nie add. Dlatego onResumew niektórych przypadkach potrzeba zniknie.


0

Wywołanie ft.replacepowinno wyzwalać onPause (zastępowanego fragmentu) i onResume (zastępującego fragmentu).

Zauważyłem, że twój kod zawyża login_fragmentfragment domowy, a także nie zwraca widoków w onCreateView. Jeśli są to literówki, czy możesz pokazać, jak te fragmenty są wywoływane z Twojej aktywności?


To nie jest odpowiedź, co jeśli „dodaj” jest konieczne w czyimś projekcie ?!
Farid

0

Chociaż z innym kodem, doświadczyłem tego samego problemu co OP, ponieważ pierwotnie użyłem

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

zamiast

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

Przy „replace” pierwszy fragment jest odtwarzany po powrocie z drugiego fragmentu, dlatego też wywoływana jest również funkcja onResume ().


To nie jest odpowiedź, co jeśli „dodaj” jest konieczne w czyimś projekcie ?!
Farid

-2

Podczas tworzenia transakcji fragmentu pamiętaj o dodaniu następującego kodu.

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

Upewnij się także, aby zatwierdzić transakcję po dodaniu jej do backstacka

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.