W systemie Android 4.2 biblioteka obsługi otrzymała obsługę zagnieżdżonych fragmentów, patrz tutaj . Bawiłem się tym i znalazłem interesujące zachowanie / błąd dotyczący stosu wstecznego i metody getChildFragmentManager () . Używając getChildFragmentManager () i addToBackStack (nazwa ciągu), po naciśnięciu przycisku Wstecz system nie przesuwa stosu wstecznego do poprzedniego fragmentu. Z drugiej strony, korzystając z getFragmentManager () i addToBackStack (nazwa ciągu), po naciśnięciu przycisku wstecz system powraca do poprzedniego fragmentu.
Dla mnie takie zachowanie jest nieoczekiwane. Naciskając przycisk Wstecz na moim urządzeniu, spodziewam się, że ostatni dodany fragment do tylnego stosu zostanie usunięty, nawet jeśli fragment został dodany do tylnego stosu w menedżerze fragmentów dla dzieci.
Czy to zachowanie jest prawidłowe? Czy to zachowanie jest błędem? Czy istnieje obejście tego problemu?
przykładowy kod z getChildFragmentManager ():
public class FragmentceptionActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
final FrameLayout wrapper1 = new FrameLayout(this);
wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper1.setId(1);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 0;
final TextView text = new TextView(this);
text.setLayoutParams(params);
text.setText("fragment 1");
wrapper1.addView(text);
setContentView(wrapper1);
getSupportFragmentManager().beginTransaction().addToBackStack(null)
.add(1, new Fragment1()).commit();
}
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper2 = new FrameLayout(getActivity());
wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper2.setId(2);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 100;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 2");
wrapper2.addView(text);
return wrapper2;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(2, new Fragment2()).commit();
}
}
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper3 = new FrameLayout(getActivity());
wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper3.setId(3);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 200;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 3");
wrapper3.addView(text);
return wrapper3;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getChildFragmentManager().beginTransaction().addToBackStack(null)
.add(3, new Fragment3()).commit();
}
}
public class Fragment3 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper4 = new FrameLayout(getActivity());
wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper4.setId(4);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 300;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 4");
wrapper4.addView(text);
return wrapper4;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getChildFragmentManager().beginTransaction().addToBackStack(null)
.add(4, new Fragment4()).commit();
}
}
public class Fragment4 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper5 = new FrameLayout(getActivity());
wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper5.setId(5);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 400;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 5");
wrapper5.addView(text);
return wrapper5;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
}
przykładowy kod z getFragmentManager ():
public class FragmentceptionActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
final FrameLayout wrapper1 = new FrameLayout(this);
wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper1.setId(1);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 0;
final TextView text = new TextView(this);
text.setLayoutParams(params);
text.setText("fragment 1");
wrapper1.addView(text);
setContentView(wrapper1);
getSupportFragmentManager().beginTransaction().addToBackStack(null)
.add(1, new Fragment1()).commit();
}
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper2 = new FrameLayout(getActivity());
wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper2.setId(2);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 100;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 2");
wrapper2.addView(text);
return wrapper2;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(2, new Fragment2()).commit();
}
}
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper3 = new FrameLayout(getActivity());
wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper3.setId(3);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 200;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 3");
wrapper3.addView(text);
return wrapper3;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(3, new Fragment3()).commit();
}
}
public class Fragment3 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper4 = new FrameLayout(getActivity());
wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper4.setId(4);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 300;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 4");
wrapper4.addView(text);
return wrapper4;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(4, new Fragment4()).commit();
}
}
public class Fragment4 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper5 = new FrameLayout(getActivity());
wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper5.setId(5);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 400;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 5");
wrapper5.addView(text);
return wrapper5;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
}
Odpowiedzi:
To rozwiązanie może być lepszą wersją odpowiedzi @Sean:
Ponownie przygotowałem to rozwiązanie na podstawie odpowiedzi @Sean powyżej.
Jak powiedział @ AZ13, to rozwiązanie jest możliwe tylko w jednopoziomowych sytuacjach fragmentów potomnych. W przypadku fragmentów wielopoziomowych prace stają się trochę skomplikowane, więc polecam wypróbować to rozwiązanie tylko w przypadku wykonalnym, który powiedziałem. =)
Uwaga: ponieważ
getFragments
metoda jest teraz metodą prywatną, to rozwiązanie nie będzie działać. W komentarzach możesz znaleźć link, który sugeruje rozwiązanie tej sytuacji.źródło
getFragments
jest teraz publiczny.Wygląda na błąd. Spójrz na: http://code.google.com/p/android/issues/detail?id=40323
Aby obejść problem, z którego pomyślnie skorzystałem (zgodnie z sugestią w komentarzach):
źródło
Prawdziwa odpowiedź na to pytanie znajduje się w funkcji Fragment Transaction o nazwie setPrimaryNavigationFragment.
Musisz ustawić tę funkcję na początkowym fragmencie nadrzędnym, gdy aktywność go dodaje. W mojej aktywności mam funkcję replaceFragment, która wygląda następująco:
Daje to takie samo zachowanie, jak w przypadku klikania z powrotem ze zwykłego fragmentu B z powrotem do fragmentu A, z wyjątkiem tego, że teraz dotyczy to również fragmentów potomnych!
źródło
Takim rozwiązaniem może być lepsza wersja odpowiedzi @ismailarilik:
Wersja zagnieżdżonego fragmentu
źródło
SupportFragmentManager
a zamiast tego tylko StandardFragmentManager
?Z tą odpowiedzią zajmie się rekurencyjnym sprawdzaniem wstecznym i da każdemu fragmentowi szansę na zastąpienie domyślnego zachowania. Oznacza to, że możesz mieć fragment, w którym znajduje się ViewPager, wykonujący coś specjalnego, na przykład przewijanie do strony, która jest na stosie wstecznym, lub przewijanie do strony głównej, a następnie na następnym cofnięciu naciśnij exit.
Dodaj to do swojego działania, które rozszerza AppCompatActivity.
Dodaj to do swojego BaseFragment lub klasy, z której możesz dziedziczyć wszystkie swoje fragmenty.
źródło
Ten kod będzie nawigował po drzewie menedżerów fragmentów i zwróci ostatni, który został dodany, zawierający fragmenty, które można wyskoczyć ze stosu:
Wywołaj metodę:
źródło
Powodem jest to, że Twoja aktywność pochodzi od FragmentActivity, która obsługuje naciśnięcie klawisza BACK (zobacz wiersz 173 fragmentu FragmentActivity .
W naszej aplikacji używam ViewPagera (z fragmentami) i każdy fragment może mieć zagnieżdżone fragmenty. Sposób, w jaki sobie z tym poradziłem, polega na:
void onBackKeyPressed()
Zauważ również, że używam
getChildFragmentManager()
we fragmentach do prawidłowego zagnieżdżenia fragmentów. Możesz zobaczyć dyskusję i wyjaśnienie w tym poście dla programistów Androida .źródło
jeśli używasz fragmentu we fragmencie, użyj
getChildFragmentManager()
jeśli używasz fragmentu potomnego i zamień go użyj
getParentFragmentManager()
jeśli oba nie działają, spróbuj tego
getActivity().getSupportFragmentManager()
źródło
Byłem w stanie obsłużyć stos fragmentów, dodając do fragmentu nadrzędnego tę metodę w metodzie onCreate View () i przekazując widok główny.
Metoda isEnableFragmentBackStack () jest flagą logiczną, która informuje, kiedy znajduję się w głównym lub następnym fragmencie.
Upewnij się, że po zatwierdzeniu fragmentu, który ma mieć stos, musisz dodać metodę addToBackstack.
źródło
To rozwiązanie może być lepsze, bo sprawdza wszystkie poziomy zagnieżdżonych fragmentów:
w swojej aktywności
onBackPressed
możesz to po prostu nazwać w ten sposób:źródło
Dziękuję wszystkim za pomoc, ta (poprawiona wersja) działa dla mnie:
UWAGA: Prawdopodobnie będziesz również chciał ustawić kolor tła tych zagnieżdżonych fragmentów na kolor tła okna motywu aplikacji, ponieważ domyślnie są one przezroczyste. Nieco poza zakresem tego pytania, ale można to osiągnąć, rozwiązując atrybut android.R.attr.windowBackground i ustawiając tło widoku fragmentu na ten identyfikator zasobu.
źródło
Ponad 5 lat i ten problem jest nadal aktualny. Jeśli nie chcesz używać funkcji fragmentManager.getFragments () ze względu na jej ograniczenie. Rozszerz i używaj poniższych klas:
NestedFragmentActivity.java
NestedFragment.java
Wyjaśnienie:
Każde działanie i fragment rozszerzony z powyższych klas zarządza własnym stosem tylnym dla każdego z ich dzieci i dzieci dzieci i tak dalej. Backstack to po prostu zapis tagów / identyfikatorów „aktywnych fragmentów”. Dlatego zastrzeżeniem jest zawsze dostarczanie tagu i / lub identyfikatora dla twojego fragmentu.
Dodając do backstack w childFragmentManager, będziesz musiał także wywołać „addToParentBackStack ()”. Gwarantuje to, że znacznik / identyfikator fragmentu zostanie dodany do nadrzędnego fragmentu / aktywności dla późniejszych wyskakujących okienek.
Przykład:
źródło
Zaimplementowałem to poprawnie, jeśli do tej pory nikt nie znalazł odpowiedzi
po prostu dodaj tę metodę do zagnieżdżonego fragmentu dziecka
źródło
Rozwiązałem ten problem, zachowując aktualnie otwarty fragment we właściwościach działalności. Następnie nadpisuję metodę
W ten sposób dodaję fragment potomny do aktualnie otwartego fragmentu z samego siebie.
To jest układ xml fragmentu nadrzędnego i dodanego fragmentu
źródło
Po zapoznaniu się z niektórymi przedstawionymi tutaj rozwiązaniami stwierdziłem, że aby zapewnić elastyczność i kontrolę nad fragmentem nadrzędnym co do tego, kiedy wyskakuje stos lub kiedy należy zignorować akcję wsteczną, raczej używam takiej implementacji:
Definiowanie interfejsu „ParentFragment”:
}
Zastępowanie „onBackPressed” w działaniu nadrzędnym (lub w BaseActivity):
A następnie pozwól, aby fragment nadrzędny działał tak, jak sobie życzy, na przykład:
Pozwala to uniknąć myślenia, że istnieje jakiś uzasadniony fragment potomny do usunięcia, na przykład podczas korzystania z pagera widoku (i liczba wpisów na stosie wstecznym> 0).
źródło
Jeśli masz,
DialogFragment
który z kolei ma zagnieżdżone fragmenty, „obejście” jest nieco inne. Zamiast ustawiaćonKeyListener
narootView
, musisz to zrobić zDialog
. Będziesz także konfigurować jeden,DialogInterface.OnKeyListener
a nie tenView
. Oczywiście pamiętajaddToBackStack
!Oto, co musisz zrobić w onCreateDialog
źródło
Dialog
(nieDialogFragment
), a odbiornik, którego używasz, toDialogInterface.OnKeyListener
- kod działa i jest kompletny (i jest w użyciu). Dlaczego nie przedstawisz swojej sytuacji i może pomogę.w przypadku ChildFragments to działa.
źródło