Mam ogromny problem ze sposobem, w jaki wydaje się działać backstack fragmentów Androida i byłbym bardzo wdzięczny za każdą oferowaną pomoc.
Wyobraź sobie, że masz 3 fragmenty
[1] [2] [3]
Chcę, aby użytkownik mógł nawigować, [1] > [2] > [3]
ale w drodze powrotnej (naciskając przycisk Wstecz) [3] > [1]
.
Jak sobie wyobrażałem, byłoby to osiągnięte bez wywoływania addToBackStack(..)
podczas tworzenia transakcji, która wprowadza fragment [2]
do posiadacza fragmentu zdefiniowanego w XML.
W rzeczywistości wydaje mi się, że jeśli nie chcę [2]
się pojawiać ponownie, gdy użytkownik naciśnie przycisk powrotu [3]
, nie mogę wywoływać addToBackStack
transakcji, która pokazuje fragment [3]
. Wydaje się to całkowicie sprzeczne z intuicją (być może pochodzi ze świata iOS).
W każdym razie, jeśli zrobię to w ten sposób, kiedy wyjdę z [1] > [2]
i wrócę, wrócę [1]
zgodnie z oczekiwaniami.
Jeśli pójdę [1] > [2] > [3]
i cofnę się, wskoczę z powrotem do [1]
(zgodnie z oczekiwaniami). Teraz dziwne zachowanie ma miejsce, gdy próbuję [2]
ponownie przeskoczyć do [1]
. Przede wszystkim [3]
jest krótko wyświetlany, zanim [2]
pojawi się w widoku. Jeśli naciśnę wstecz w tym momencie, [3]
zostanie wyświetlony komunikat, a jeśli naciśnę ponownie, aplikacja zostanie zamknięta.
Czy ktoś może mi pomóc zrozumieć, co się tutaj dzieje?
A oto plik xml układu dla mojej głównej działalności:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<fragment
android:id="@+id/headerFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
class="com.fragment_test.FragmentControls" >
<!-- Preview: layout=@layout/details -->
</fragment>
<FrameLayout
android:id="@+id/detailFragment"
android:layout_width="match_parent"
android:layout_height="fill_parent"
/>
Aktualizacja To jest kod, którego używam do tworzenia według heirarchii nawigacyjnej
Fragment frag;
FragmentTransaction transaction;
//Create The first fragment [1], add it to the view, BUT Dont add the transaction to the backstack
frag = new Fragment1();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.commit();
//Create the second [2] fragment, add it to the view and add the transaction that replaces the first fragment to the backstack
frag = new Fragment2();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.addToBackStack(null);
transaction.commit();
//Create third fragment, Dont add this transaction to the backstack, because we dont want to go back to [2]
frag = new Fragment3();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.commit();
//END OF SETUP CODE-------------------------
//NOW:
//Press back once and then issue the following code:
frag = new Fragment2();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detailFragment, frag);
transaction.addToBackStack(null);
transaction.commit();
//Now press back again and you end up at fragment [3] not [1]
Wielkie dzięki
źródło
Odpowiedzi:
Wyjaśnienie: o co tu chodzi?
Jeśli pamiętamy, że
.replace()
jest to równe.remove().add()
temu, które znamy z dokumentacji:to, co się dzieje, wygląda tak (dodaję liczby do fragmentu, aby było bardziej zrozumiałe):
(tutaj zaczynają się dziać wszystkie wprowadzające w błąd rzeczy)
Pamiętaj, że
.addToBackStack()
zapisuje tylko transakcję, a nie fragment jako siebie! Więc teraz mamyfrag3
na układzie:Możliwe rozwiązanie
Rozważ wdrożenie,
FragmentManager.BackStackChangedListener
aby obserwować zmiany w stosie wstecznym i zastosować swoją logikę wonBackStackChanged()
metodzie:FragmentTransaction.addToBackStack(String name);
źródło
FragmentManager.BackStackChangedListener
aby obserwować zmiany w stosie tylnym. Monitoruj wszystkie swoje transakcjeonBackStackChanged()
metodą i działaj w razie potrzeby: np. śledzić liczbę transakcji w BackStack; sprawdź konkretną transakcję po nazwie (FragmentTransaction addToBackStack (String name)
) itd.Dobrze!!! po wielu wyrywaniu włosów w końcu wymyśliłem, jak to działa.
Wygląda na to, że fragment [3] nie jest usuwany z widoku po naciśnięciu przycisku wstecz, więc musisz to zrobić ręcznie!
Przede wszystkim nie używaj replace (), ale zamiast tego użyj usuń i dodaj oddzielnie. Wygląda na to, że funkcja replace () nie działa poprawnie.
Następną częścią jest zastąpienie metody onKeyDown i usunięcie bieżącego fragmentu za każdym razem, gdy naciśnięty zostanie przycisk Wstecz.
Mam nadzieję że to pomoże!
źródło
Przede wszystkim dziękuję @Arvis za otwierające oczy wyjaśnienie.
Wolę inne rozwiązanie niż zaakceptowana tutaj odpowiedź na ten problem. Nie lubię majstrować przy zastępowaniu zachowania wstecz, bardziej niż jest to absolutnie konieczne, a kiedy próbowałem samodzielnie dodawać i usuwać fragmenty bez domyślnego wyskakującego stosu po naciśnięciu przycisku Wstecz, znalazłem się w piekle fragmentów :) Jeśli ty. dodaj f2 do f1, gdy go usuniesz, f1 nie wywoła żadnej z metod wywołania zwrotnego, takich jak onResume, onStart itp. i może to być bardzo niefortunne.
W każdym razie tak to robię:
Obecnie na wyświetlaczu jest tylko fragment f1.
f1 -> f2
nic niezwykłego tutaj. Następnie we fragmencie f2 ten kod prowadzi do fragmentu f3.
f2 -> f3
Nie jestem pewien, czy czytając dokumentację, czy to powinno zadziałać, mówi się, że ta metoda transakcji poping jest asynchroniczna i być może lepszym sposobem byłoby wywołanie popBackStackImmediate (). Ale o ile mogę stwierdzić, na moich urządzeniach działa bez zarzutu.
Wspomniana alternatywa to:
Tutaj faktycznie będzie krótki powrót do f1 przed przejściem do f3, więc tam drobny błąd.
To właściwie wszystko, co musisz zrobić, nie ma potrzeby zastępowania zachowania stosu wstecznego ...
źródło
Wiem, że to stara kwestia, ale mam ten sam problem i naprawię go w ten sposób:
Najpierw dodaj Fragment1 do BackStack z nazwą (np. „Frag1”):
A potem, ilekroć chcesz wrócić do Fragmentu1 (nawet po dodaniu 10 fragmentów nad nim), po prostu wywołaj popBackStackImmediate z nazwą:
Mam nadzieję, że to komuś pomoże :)
źródło
Po odpowiedzi @Arvis postanowiłem poszukać jeszcze głębiej i napisałem artykuł techniczny na ten temat tutaj: http://www.andreabaccega.com/blog/2015/08/16/how-to-avoid-fragments-overlapping- z powodu koszmaru-backstack-in-android /
Dla leniwych programistów dookoła. Moje rozwiązanie polega na zawsze dodawaniu transakcji do backstacka i wykonywaniu dodatkowych
FragmentManager.popBackStackImmediate()
gdy jest to potrzebne (automatycznie).Kod składa się z bardzo niewielu wierszy kodu, aw moim przykładzie chciałem przeskoczyć z C do A bez przeskakiwania z powrotem do „B”, jeśli użytkownik nie wszedł głębiej w backstack (np. Z C przechodzi do D).
Stąd załączony kod działałby następująco A -> B -> C (tył) -> A & A -> B -> C -> D (tył) -> C (tył) -> B (tył) -> A
gdzie
zostały wydane od „B” do „C” jak w pytaniu.
Ok, ok tu jest kod :)
źródło
Jeśli zmagasz się z addToBackStack () i popBackStack (), po prostu użyj
W swojej aktywności w OnBackPressed () znajdź fargowanie według tagu, a następnie rób swoje
Aby uzyskać więcej informacji https://github.com/DattaHujare/NavigationDrawer Nigdy nie używam addToBackStack () do obsługi fragmentu.
źródło
Myślę, że kiedy czytam twoją historię, [3] jest również na zapleczu. To wyjaśnia, dlaczego widzisz, jak miga.
Rozwiązaniem byłoby nigdy nie umieszczać [3] na stosie.
źródło
s old method issue but now it
porządku. DziękiMiałem podobny problem, gdzie miałem 3 kolejne fragmenty w tym samym
Activity
[M1.F0] -> [M1.F1] -> [M1.F2], po których następowało wezwanie do nowegoActivity
[M2]. Jeśli użytkownik nacisnął przycisk w [M2], chciałem wrócić do [M1, F1] zamiast do [M1, F2], co już robiło zachowanie wstecznego naciśnięcia.W tym celu usuwam [M1, F2], wywołuję show na [M1, F1], zatwierdzam transakcję, a następnie dodaję z powrotem [M1, F2], wywołując ją z użyciem funkcji hide. Spowodowało to usunięcie dodatkowej prasy wstecznej, która w przeciwnym razie zostałaby pozostawiona.
Cześć Po wykonaniu tego kodu: Nie mogę zobaczyć wartości Fragment2 po naciśnięciu klawisza Wstecz. Mój kod:
źródło
executePendingTransactions()
,commitNow()
nie pracował (Pracował w systemie Androidx (jetpack).
źródło