Jak mogę zachować stan fragmentu po dodaniu do stosu tylnego?

160

Napisałem sztuczną aktywność, która przełącza się między dwoma fragmentami. Kiedy przechodzisz z Fragmentu A do Fragmentu B, FragmentA zostaje dodany do tylnego stosu. Jednak kiedy wracam do Fragmentu A (naciskając wstecz), tworzony jest całkowicie nowy Fragment A, a stan, w którym był, zostaje utracony. Mam wrażenie, że szukam tego samego, co to pytanie, ale dołączyłem pełny przykład kodu, aby pomóc wykorzenić problem:

public class FooActivity extends Activity {
  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentA());
    transaction.commit();
  }

  public void nextFragment() {
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentB());
    transaction.addToBackStack(null);
    transaction.commit();
  }

  public static class FragmentA extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      final View main = inflater.inflate(R.layout.main, container, false);
      main.findViewById(R.id.next_fragment_button).setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
          ((FooActivity) getActivity()).nextFragment();
        }
      });
      return main;
    }

    @Override public void onSaveInstanceState(Bundle outState) {
      super.onSaveInstanceState(outState);
      // Save some state!
    }
  }

  public static class FragmentB extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      return inflater.inflate(R.layout.b, container, false);
    }
  }
}

Po dodaniu niektórych komunikatów dziennika:

07-05 14:28:59.722 D/OMG     ( 1260): FooActivity.onCreate
07-05 14:28:59.742 D/OMG     ( 1260): FragmentA.onCreateView
07-05 14:28:59.742 D/OMG     ( 1260): FooActivity.onResume
<Tap Button on FragmentA>
07-05 14:29:12.842 D/OMG     ( 1260): FooActivity.nextFragment
07-05 14:29:12.852 D/OMG     ( 1260): FragmentB.onCreateView
<Tap 'Back'>
07-05 14:29:16.792 D/OMG     ( 1260): FragmentA.onCreateView

Nigdy nie wywołuje FragmentA.onSaveInstanceState i tworzy nowy FragmentA po uderzeniu z powrotem. Jeśli jednak jestem na FragmentA i zablokuję ekran, zostanie wywołany FragmentA.onSaveInstanceState. To dziwne ... czy mylę się, spodziewając się, że fragment dodany do tylnego stosu nie będzie wymagał ponownego tworzenia? Oto, co mówią dokumenty :

Natomiast jeśli wywołasz addToBackStack () podczas usuwania fragmentu, to fragment zostanie zatrzymany i zostanie wznowiony, jeśli użytkownik nawiguje wstecz.

Eric
źródło
3
@ Jan-Henk A co z rzeczami, które trzeba przywieźć? Na przykład pozycja przewijania pliku ListView. Wygląda na to, że zbyt dużo przeskakiwania do kółka, aby dołączyć nasłuchiwanie przewijania i zaktualizować zmienną instancji.
Jake Wharton,
2
@JakeWharton Zgadzam się, że powinno być łatwiej, ale o ile wiem, nie da się tego obejść, ponieważ onCreateView jest wywoływana, gdy fragment jest przywracany z backstack. Ale mogę się mylić :)
Jan-Henk
1
onCreate nie jest wywoływany. Więc najwyraźniej ponownie używa tego samego wystąpienia, ale ponownie wywołuje onCreateView? Kulawy. Myślę, że mogę po prostu buforować wynik onCreateView i po prostu zwrócić istniejący widok, jeśli onCreateView zostanie ponownie wywołany.
Eric,
1
Dokładnie to, czego szukałem godzinami. Czy możesz opublikować, w jaki sposób osiągnąłeś to za pomocą zmiennej instancji?
Uma
1
Niedawno zacząłem własną implementację na github.com/frostymarvellous/Folio i napotkałem problem. Jestem w stanie utworzyć około 5 złożonych stron / fragmentów, zanim zacznę otrzymywać awarie OOM. To mnie tu przywiodło. Ukrywanie się i pokazywanie po prostu nie wystarczy. Widoki są zbyt obciążone pamięcią.
mroźno cudowny

Odpowiedzi:

120

Jeśli wrócisz do fragmentu z tylnego stosu, nie utworzy on ponownie fragmentu, ale ponownie użyje tego samego wystąpienia i rozpocznie się onCreateView()w cyklu życia fragmentu, zobacz Cykl życia fragmentu .

Więc jeśli chcesz przechowywać stan, powinieneś używać zmiennych instancji i nie polegać na nich onSaveInstanceState().

Jan-Henk
źródło
32
Obecna wersja dokumentacji zaprzecza temu twierdzeniu. Schemat blokowy mówi, co podasz, ale tekst w głównym obszarze strony mówi, że onCreateView () jest wywoływany tylko przy pierwszym wyświetleniu fragmentu: developer.android.com/guide/components/fragments.html Walczę z tym problem teraz i nie widzę żadnych metod wywoływanych podczas zwracania fragmentu z zaplecza. (Android 4.2)
Colin M.
10
Próbowałem zarejestrować jego zachowanie. Metoda onCreateView () jest zawsze wywoływana, gdy fragment jest wyświetlany.
princepiero
4
@ColinM. Jakieś rozwiązanie problemu?
zamieć
9
To nie działa na mnie. Moje zmienne instancji są zerowe po powrocie do fragmentu! Jak mogę uratować stan?
Don Rhummy
5
więc jeśli nie powinniśmy polegać na instancji zapisywania, w jaki sposób należy zapisać stan fragmentu i dane?
Mahdi
80

W porównaniu do Apple UINavigationControlleri UIViewControllerGoogle nie radzi sobie dobrze w architekturze oprogramowania Android. Dokument na temat Androida Fragmentniewiele pomaga.

Po wprowadzeniu FragmentB z FragmentA istniejące wystąpienie FragmentA nie zostanie zniszczone. Po naciśnięciu przycisku Back w FragmentB i powrocie do FragmentA nie tworzymy nowej instancji FragmentA. Istniejące wystąpienia FragmentAonCreateView() wywołana .

Kluczową sprawą jest to, że nie powinniśmy ponownie zawyżać widoku w FragmentA onCreateView() , ponieważ używamy istniejącej instancji FragmentA. Musimy zapisać i ponownie użyć rootView.

Poniższy kod działa dobrze. Nie tylko zachowuje stan fragmentów, ale także zmniejsza obciążenie pamięci RAM i procesora (ponieważ nadmuchujemy układ tylko w razie potrzeby). Nie mogę uwierzyć, że przykładowy kod i dokument Google nigdy o tym nie wspominają, ale zawsze zawyżają układ .

Wersja 1 (nie używaj wersji 1. Użyj wersji 2)

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // (it will be added back).
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        return _rootView;
    }
}

------ Aktualizacja 3 maja 2005 r .: -------

Jak wspomniano w komentarzach, czasami _rootView.getParent()jest pustyonCreateView , co powoduje awarię. Wersja 2 usuwa _rootView w onDestroyView (), zgodnie z sugestią dell116. Przetestowano na Androidzie 4.0.3, 4.4.4, 5.1.0.

Wersja 2

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // in onDestroyView() (it will be added back).
        }
        return _rootView;
    }

    @Override
    public void onDestroyView() {
        if (_rootView.getParent() != null) {
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        super.onDestroyView();
    }
}

OSTRZEŻENIE!!!

To jest HACK! Chociaż używam go w mojej aplikacji, musisz uważnie przetestować i przeczytać komentarze.

Vince Yuan
źródło
38
Posiadanie odniesienia do widoku głównego całego fragmentu jest złym pomysłem IMO. Jeśli ciągle dodajesz kilka fragmentów do backstacka i wszystkie z nich zachowują swój widok główny (który ma dość duży ślad pamięci), istnieje duże prawdopodobieństwo, że skończysz z OutOfMemoryError, ponieważ wszystkie fragmenty zawierają odniesienie do rootview i przechyłkę GC Zbierz to. Myślę, że lepszym podejściem jest ciągłe zawyżanie widoku (i pozwolenie systemowi Android na obsługę tworzenia / niszczenia widoku) i sprawdzanie onActivityCreated / onViewCreated, czy dane są puste. Jeśli tak, załaduj go, w przeciwnym razie ustaw dane na widoki.
traninho
15
Nie rób tego! Po utworzeniu hierarchii widoków fragmentu zawiera ona wewnętrzne odniesienie do działania, które w danym momencie zawierało fragment. W przypadku zmiany konfiguracji działanie jest często odtwarzane. Ponowne użycie starego układu zachowuje tę aktywność zombie w pamięci wraz z obiektami, do których również się odnosi. Takie marnowanie pamięci zmniejsza wydajność i sprawia, że ​​Twoja aplikacja jest najlepszym kandydatem do natychmiastowego usunięcia, gdy nie jest na pierwszym planie.
Krylez
4
@AllDayAmazing To dobra uwaga. Szczerze mówiąc, jestem teraz bardzo zdezorientowany. Czy ktokolwiek może spróbować wyjaśnić, dlaczego trzymanie odniesienia do rootview fragmentu nie jest w porządku, ale trzymanie odniesienia tylko dla dowolnego elementu potomnego rootview (które i tak ma odniesienie do rootview) jest w porządku?
traninho
2
UTRZYMAJ SIĘ OD TEGO, chyba że chcesz stracić 5 godzin na szukanie błędów w Twoim kodzie ..... a potem tylko po to, by dowiedzieć się, że to jest przyczyna. Teraz muszę przerobić kilka rzeczy, ponieważ użyłem tego hacka. Znacznie lepiej jest użyć fragmentTransaction.add, jeśli chcesz, aby interfejs użytkownika fragmentu był nietaktowny podczas wyświetlania innego (nawet na górze). fragmentTransaction.replace () ma na celu zniszczenie widoków fragmentu ..... nie walcz z systemem.
dell116
2
@VinceYuan - Testowałem z najnowszą biblioteką v7-appcompat w systemie Android 5.1 i pozostawiło to 6 wystąpień fragmentu, który powinien zostać usunięty w FragmentManager mojej aktywności. Nawet jeśli GC poradzi sobie z tym poprawnie (co, jak sądzę, nie będzie), powoduje to niepotrzebne obciążenie pamięci dla Twojej aplikacji, a także ogólnie dla urządzenia. Po prostu użycie .add () całkowicie eliminuje potrzebę stosowania całego tego hakerskiego kodu. Jest to całkowicie sprzeczne z tym, co miało zrobić użycie FragmentTransaction.replace ().
dell116
53

Myślę, że istnieje alternatywny sposób na osiągnięcie tego, czego szukasz. Nie mówię, że to kompletne rozwiązanie, ale w moim przypadku spełniło to zadanie.

To, co zrobiłem, to zamiast zastąpić fragment, który właśnie dodałem fragment docelowy. Więc zasadniczo użyjesz add()metody zamiast tegoreplace() .

Co jeszcze zrobiłem. Ukrywam mój aktualny fragment, a także dodaję go do stosu.

W związku z tym nakłada nowy fragment na bieżący fragment bez niszczenia jego widoku. (Sprawdź, czy jego onDestroyView()metoda nie jest wywoływana. Plus dodanie go dobackstate daje mi przewagę w postaci wznowienia fragmentu.

Oto kod:

Fragment fragment=new DestinationFragment();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction ft=fragmentManager.beginTransaction();
ft.add(R.id.content_frame, fragment);
ft.hide(SourceFragment.this);
ft.addToBackStack(SourceFragment.class.getName());
ft.commit();

AFAIK System tylko dzwoni onCreateView() wtedy, gdy widok jest zniszczony lub nie został utworzony. Ale tutaj zapisaliśmy widok, nie usuwając go z pamięci. Więc nie utworzy nowego widoku.

A kiedy wrócisz z Destination Fragment, wyskoczy ostatni FragmentTransaction usuwany górny fragment, co spowoduje, że najwyższy widok (SourceFragment) pojawi się na ekranie.

KOMENTARZ: Jak powiedziałem, nie jest to rozwiązanie kompletne, ponieważ nie usuwa widoku fragmentu Źródła i przez to zajmuje więcej pamięci niż zwykle. Ale mimo wszystko, służ celowi. Ponadto używamy zupełnie innego mechanizmu ukrywania widoku zamiast go zastępować, co jest nietradycyjne.

Więc tak naprawdę nie chodzi o to, jak utrzymujesz stan, ale o to, jak utrzymujesz widok.

kaushal trivedi
źródło
W moim przypadku dodanie fragmentu zamiast zastępowania powoduje problem podczas korzystania z odpytywania lub innego rodzaju żądania internetowego we fragmencie. Chcę wstrzymać to sondowanie we fragmencie A po dodaniu fragmentu B. Masz jakiś pomysł na to?
Uma
Jak używasz odpytywania w FirstFragment? Musisz to zrobić ręcznie, ponieważ oba fragmenty pozostają w pamięci, więc możesz wykorzystać ich instancje do wykonania niezbędnej akcji. - oto wskazówka, wygeneruj zdarzenie w głównym działaniu, które zrobi coś po dodaniu drugiego fragmentu. Mam nadzieję, że to pomoże.
kaushal trivedi
1
Dziękuję za wskazówkę =). Zrobiłem to. Ale czy to jedyny sposób, aby to zrobić? A właściwy sposób? Również po naciśnięciu przycisku home i ponownym uruchomieniu aplikacji wszystkie fragmenty znów stają się aktywne. Załóżmy, że jestem tutaj we fragmencie B w ten sposób. Activity A{Fragment A --> Fragment B}kiedy ponownie uruchamiam aplikację po naciśnięciu przycisku home, onResume()wywoływane są oba fragmenty i tym samym rozpoczynają swoje odpytywanie. Jak mogę to kontrolować?
Uma
1
Niestety nie możesz, system nie działa w normalnym zachowaniu w ten sposób, uzna oba fragmenty za bezpośrednie dziecko aktywności. Chociaż służyło to utrzymaniu stanu fragmentu, inne normalne rzeczy stają się bardzo trudne do zarządzania. odkryłem wszystkie te problemy, teraz moja sugestia jest taka, aby nie iść w ten sposób. przepraszam.
kaushal trivedi
1
Oczywiście na koniec powiem, aby nie stosować tego podejścia, dopóki nie znajdziesz innego rozwiązania, ponieważ jest to trudne do zarządzania.
kaushal trivedi
7

Proponuję bardzo proste rozwiązanie.

Weź zmienną referencyjną widoku i ustaw widok w OnCreateView. Sprawdź, czy widok już istnieje w tej zmiennej, a następnie zwróć ten sam widok.

   private View fragmentView;

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

        if (fragmentView != null) {
            return fragmentView;
        }
        View view = inflater.inflate(R.layout.yourfragment, container, false);
        fragmentView = view;
        return view;
    }
Mandeep Singh
źródło
1
Istnieje szansa na przeciek pamięci, jeśli nie usuniemy zmiennej „fragmentView” w onDestroy ()
Arun PM
@ArunPM więc jak usunąć fragmentView w onDestroy ()? if (_rootView.getParent() != null) { ((ViewGroup)_rootView.getParent()).removeView(_rootView); }jest właściwe, aby wyczyścić pamięć?
Mehmet Gür
1
@ MehmetGür Korzystam z tego rozwiązania wiele razy. Do tej pory nie otrzymałem żadnego błędu wycieku pamięci. Ale możesz użyć z tym rozwiązania ArunPM, jeśli chcesz. Myślę, że mówi, aby ustawić fragmentView na null w metodzie OnDestroy ().
Mandeep Singh
1
Używam LeakCanary do wykrywania wycieków pamięci i problemów z wyrzucaniem wycieków, gdy stosowałem tę metodę. Ale jak @Mandeep Sigh wspomniał w komentarzu, możemy rozwiązać ten problem, przypisując nulldo fragmentView zmiennej w onDestroy()metodzie.
Arun PM
1
Zgodnie z moją wiedzą, gdy fragment ulegający zniszczeniu, zostaje oczyszczony z widoku związanego z tym fragmentem onDestroyView(). To czyszczenie nie dotyczy naszej zmiennej widoku kopii zapasowej (tutaj fragmentView ) i spowoduje wyciek pamięci, gdy fragment zostanie z powrotem ułożony w stos / zniszczony. To samo odniesienie można znaleźć w [Typowe przyczyny wycieków pamięci] ( square.github.io/leakcanary/fundamentals/… ) we wprowadzeniu do LeakCanery.
Arun PM
6

Natknąłem się na ten problem we fragmencie zawierającym mapę, która ma zbyt wiele szczegółów konfiguracji, aby zapisać / przeładować. Moim rozwiązaniem było po prostu utrzymanie tego fragmentu aktywnego przez cały czas (podobnie jak wspomniał @kaushal).

Załóżmy, że masz aktualny fragment A i chcesz wyświetlić fragment B. Podsumowując konsekwencje:

  • replace () - usuń Fragment A i zastąp go Fragmentem B. Fragment A zostanie odtworzony po ponownym przeniesieniu na wierzch
  • add () - (utwórz i) dodaj Fragment B i nakłada się na Fragment A, który jest nadal aktywny w tle
  • remove () - może służyć do usuwania fragmentu B i powrotu do A. Fragment B zostanie odtworzony po późniejszym wywołaniu

Dlatego, jeśli chcesz zachować oba fragmenty „zapisane”, po prostu przełącz je za pomocą funkcji hide () / show ().

Zalety : łatwa i prosta metoda utrzymania wielu fragmentów działających
Wady : zużywasz dużo więcej pamięci, aby wszystkie działały. Może napotkać problemy, np. Wyświetlając wiele dużych bitmap

Teo Inke
źródło
czy możesz mi powiedzieć, kiedy usuwamy fragment b i wracamy do A, to która metoda jest wywoływana we fragmencie A? chcę podjąć jakieś działania, gdy usuniemy fragment B.
Google
5

onSaveInstanceState() jest wywoływana tylko w przypadku zmiany konfiguracji.

Ponieważ zmiana z jednego fragmentu na inny nie ma zmiany konfiguracji, więc nie ma wywołania onSaveInstanceState() . Który stan nie jest zbawiony? Czy możesz określić?

Jeśli wpiszesz jakiś tekst w EditText, zostanie on automatycznie zapisany. Każdy element interfejsu użytkownika bez identyfikatora to element, którego stan widoku nie zostanie zapisany.

user2779311
źródło
onSaveInstanceState()jest również wywoływana, gdy system niszczy Aktywność, ponieważ brakuje mu zasobów.
Marcel Bro
0

Tutaj, ponieważ onSaveInstanceStatein fragment nie wywołuje po dodaniu fragmentu do backstacka. Cykl życia fragmentu w stosie po przywróceniu początku onCreateViewi końca, onDestroyViewgdy onSaveInstanceStatejest wywoływany między onDestroyViewi onDestroy. Moje rozwiązanie polega na utworzeniu zmiennej instancji i init w onCreate. Przykładowy kod:

private boolean isDataLoading = true;
private ArrayList<String> listData;
public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     isDataLoading = false;
     // init list at once when create fragment
     listData = new ArrayList();
}

I sprawdź to onActivityCreated:

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    if(isDataLoading){
         fetchData();
    }else{
         //get saved instance variable listData()
    }
}

private void fetchData(){
     // do fetch data into listData
}
Ling Boo
źródło
0
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener()
    {
        @Override
        public void onBackStackChanged()
        {
            if (getSupportFragmentManager().getBackStackEntryCount() == 0)
            {
                //setToolbarTitle("Main Activity");
            }
            else
            {
                Log.e("fragment_replace11111", "replace");
            }
        }
    });


YourActivity.java
@Override
public void onBackPressed()
{
 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.Fragment_content);
  if (fragment instanceof YourFragmentName)
    {
        fragmentReplace(new HomeFragment(),"Home Fragment");
        txt_toolbar_title.setText("Your Fragment");
    }
  else{
     super.onBackPressed();
   }
 }


public void fragmentReplace(Fragment fragment, String fragment_name)
{
    try
    {
        fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.Fragment_content, fragment, fragment_name);
        fragmentTransaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
        fragmentTransaction.addToBackStack(fragment_name);
        fragmentTransaction.commitAllowingStateLoss();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}
Hardik Vasani
źródło
0

Mój problem był podobny, ale pokonałem mnie, nie utrzymując fragmentu przy życiu. Załóżmy, że masz aktywność, która ma 2 fragmenty - F1 i F2. F1 jest początkowo uruchamiany i powiedzmy, że zawiera pewne informacje o użytkowniku, a następnie pod pewnymi warunkami pojawia się F2, prosząc użytkownika o uzupełnienie dodatkowego atrybutu - jego numeru telefonu. Następnie chcesz, aby ten numer telefonu wrócił do F1 i dokończył rejestrację, ale zdajesz sobie sprawę, że wszystkie poprzednie informacje o użytkowniku zostały utracone i nie masz ich poprzednich danych. Fragment jest odtwarzany od zera i nawet jeśli zapisałeś te informacje w onSaveInstanceStatepaczce, wraca do wartości nullonActivityCreated .

Rozwiązanie: Zapisz wymagane informacje jako zmienną instancji w działaniu wywołującym. Następnie przekaż tę zmienną instancji do swojego fragmentu.

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

    Bundle args = getArguments();

    // this will be null the first time F1 is created. 
    // it will be populated once you replace fragment and provide bundle data
    if (args != null) {
        if (args.get("your_info") != null) {
            // do what you want with restored information
        }
    }
}

A więc idąc za moim przykładem: zanim wyświetlę F2, zapisuję dane użytkownika w zmiennej instancji za pomocą wywołania zwrotnego. Następnie uruchamiam F2, użytkownik wpisuje numer telefonu i naciska Zapisz. Używam innego wywołania zwrotnego w działaniu, zbieram te informacje i zastępuję mój fragment F1, tym razem ma on pakiet danych, których mogę użyć.

@Override
public void onPhoneAdded(String phone) {
        //replace fragment
        F1 f1 = new F1 ();
        Bundle args = new Bundle();
        yourInfo.setPhone(phone);
        args.putSerializable("you_info", yourInfo);
        f1.setArguments(args);

        getFragmentManager().beginTransaction()
                .replace(R.id.fragmentContainer, f1).addToBackStack(null).commit();

    }
}

Więcej informacji na temat callbacków można znaleźć tutaj: https://developer.android.com/training/basics/fragments/communicating.html

Dodi
źródło
0

pierwszy : po prostu użyj metody add zamiast metody replace klasy FragmentTransaction, następnie musisz dodać secondFragment do stosu metodą addToBackStack

po drugie : na tylnym kliknięciu musisz wywołać popBackStackImmediate ()

Fragment sourceFragment = new SourceFragment ();
final Fragment secondFragment = new SecondFragment();
final FragmentTransaction ft = getChildFragmentManager().beginTransaction();
ft.add(R.id.child_fragment_container, secondFragment );
ft.hide(sourceFragment );
ft.addToBackStack(NewsShow.class.getName());
ft.commit();
                                
((SecondFragment)secondFragment).backFragmentInstanceClick = new SecondFragment.backFragmentNewsResult()
{
        @Override
        public void backFragmentNewsResult()
        {                                    
            getChildFragmentManager().popBackStackImmediate();                                
        }
};
Milad Ahmadi
źródło
0

Zastąp fragment przy użyciu następującego kodu:

Fragment fragment = new AddPaymentFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.frame, fragment, "Tag_AddPayment")
                .addToBackStack("Tag_AddPayment")
                .commit();

Aktywność onBackPressed () to:

  @Override
public void onBackPressed() {
    android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
    if (fm.getBackStackEntryCount() > 1) {

        fm.popBackStack();
    } else {


        finish();

    }
    Log.e("popping BACKSTRACK===> ",""+fm.getBackStackEntryCount());

}
Pratibha Sarode
źródło
0
Public void replaceFragment(Fragment mFragment, int id, String tag, boolean addToStack) {
        FragmentTransaction mTransaction = getSupportFragmentManager().beginTransaction();
        mTransaction.replace(id, mFragment);
        hideKeyboard();
        if (addToStack) {
            mTransaction.addToBackStack(tag);
        }
        mTransaction.commitAllowingStateLoss();
    }
replaceFragment(new Splash_Fragment(), R.id.container, null, false);
Hitesh Manwani
źródło
1
Dziękujemy za ten fragment kodu, który może zapewnić ograniczoną, natychmiastową pomoc. Właściwe wyjaśnienie byłoby znacznie poprawić swoją długoterminową wartość pokazując dlaczego jest to dobre rozwiązanie problemu, a byłoby bardziej użyteczne dla czytelników przyszłości z innymi, podobnymi pytaniami. Proszę edytować swoją odpowiedź dodać kilka wyjaśnień, w tym założeń już wykonanych.
Machavity
0

Idealne rozwiązanie, które znajduje stary fragment w stosie i ładuje go, jeśli istnieje w stosie.

/**
     * replace or add fragment to the container
     *
     * @param fragment pass android.support.v4.app.Fragment
     * @param bundle pass your extra bundle if any
     * @param popBackStack if true it will clear back stack
     * @param findInStack if true it will load old fragment if found
     */
    public void replaceFragment(Fragment fragment, @Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        String tag = fragment.getClass().getName();
        Fragment parentFragment;
        if (findInStack && fm.findFragmentByTag(tag) != null) {
            parentFragment = fm.findFragmentByTag(tag);
        } else {
            parentFragment = fragment;
        }
        // if user passes the @bundle in not null, then can be added to the fragment
        if (bundle != null)
            parentFragment.setArguments(bundle);
        else parentFragment.setArguments(null);
        // this is for the very first fragment not to be added into the back stack.
        if (popBackStack) {
            fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        } else {
            ft.addToBackStack(parentFragment.getClass().getName() + "");
        }
        ft.replace(R.id.contenedor_principal, parentFragment, tag);
        ft.commit();
        fm.executePendingTransactions();
    }

użyj go jak

Fragment f = new YourFragment();
replaceFragment(f, null, boolean true, true); 
Khemraj
źródło