Oto scenariusz: Aktywność zawiera fragment A
, który z kolei używa getChildFragmentManager()
się do dodawania fragmenty A1
i A2
w swej onCreate
tak:
getChildFragmentManager()
.beginTransaction()
.replace(R.id.fragmentOneHolder, new FragmentA1())
.replace(R.id.fragmentTwoHolder, new FragmentA2())
.commit()
Jak na razie wszystko działa zgodnie z oczekiwaniami.
Następnie uruchamiamy następującą transakcję w Aktywności:
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(anim1, anim2, anim1, anim2)
.replace(R.id.fragmentHolder, new FragmentB())
.addToBackStack(null)
.commit()
Podczas przejścia enter
animacje dla fragmentów B
działają poprawnie, ale fragmenty A1 i A2 całkowicie znikają . Kiedy cofamy transakcję przyciskiem Wstecz, inicjalizują się one poprawnie i wyświetlają się normalnie podczas popEnter
animacji.
W moich krótkich testach zrobiło się dziwniej - jeśli ustawię animacje dla fragmentów podrzędnych (patrz poniżej), exit
animacja działa sporadycznie, gdy dodamy fragmentB
getChildFragmentManager()
.beginTransaction()
.setCustomAnimations(enter, exit)
.replace(R.id.fragmentOneHolder, new FragmentA1())
.replace(R.id.fragmentTwoHolder, new FragmentA2())
.commit()
Efekt, który chcę osiągnąć jest prosty - chcę, aby animacja na fragmencie (anim2) działała exit
(a może powinna być popExit
?), A
Animując cały kontener, w tym jego zagnieżdżone elementy podrzędne.
Czy jest jakiś sposób, aby to osiągnąć?
Edit : Proszę znaleźć przypadek testowy tutaj
Edit2 : Dziękuję @StevenByle za popychanie mnie do dalszych prób ze statycznymi animacjami. Najwyraźniej możesz ustawić animacje na zasadzie per-op (nie globalne dla całej transakcji), co oznacza, że dzieci mogą mieć nieokreślony zestaw animacji statycznej, podczas gdy ich rodzic może mieć inną animację i całość może zostać zatwierdzona w jednej transakcji . Zobacz dyskusję poniżej i zaktualizowany projekt przypadku testowego .
R.id.fragmentHolder
w odniesieniu do A, A1, A2 itd.?changeFragment
metodę tylko raz?Odpowiedzi:
Aby użytkownik nie widział, jak zagnieżdżone fragmenty znikają, gdy fragment nadrzędny jest usuwany / zastępowany w transakcji, można „zasymulować” te fragmenty, które wciąż są obecne, dostarczając ich obraz, gdy pojawiały się na ekranie. Ten obraz będzie używany jako tło dla kontenera zagnieżdżonych fragmentów, więc nawet jeśli widoki zagnieżdżonego fragmentu znikną, obraz zasymuluje ich obecność. Ponadto nie uważam utraty interaktywności z widokami zagnieżdżonego fragmentu za problem, ponieważ nie sądzę, aby użytkownik chciał, aby użytkownik działał na nich, gdy są właśnie w trakcie usuwania (prawdopodobnie jako działanie użytkownika, ponieważ dobrze).
Zrobiłem mały przykład z ustawieniem obrazu tła (coś podstawowego).
źródło
Wydaje się więc, że istnieje wiele różnych obejść tego problemu, ale w oparciu o odpowiedź @ Jayd16 myślę, że znalazłem całkiem solidne rozwiązanie typu catch-all, które nadal pozwala na niestandardowe animacje przejścia na fragmentach podrzędnych i nie wymaga robienia pamięć podręczna mapy bitowej układu.
Miej
BaseFragment
klasę, która rozszerzaFragment
, i spraw, aby wszystkie twoje fragmenty rozszerzały tę klasę (nie tylko fragmenty potomne).W tej
BaseFragment
klasie dodaj następujące informacje:Niestety wymaga refleksji; Jednak ponieważ to obejście dotyczy biblioteki pomocy technicznej, nie ryzykujesz zmiany podstawowej implementacji, chyba że zaktualizujesz bibliotekę pomocy technicznej. Jeśli tworzysz bibliotekę wsparcia ze źródła, możesz dodać akcesor dla następnego identyfikatora zasobu animacji do
Fragment.java
i usunąć potrzebę refleksji.To rozwiązanie eliminuje potrzebę "zgadywania" czasu trwania animacji rodzica (tak, że animacja "nic nie rób" będzie miała taki sam czas trwania jak animacja wyjścia rodzica) i nadal pozwala na tworzenie niestandardowych animacji na fragmentach potomnych (np. Jeśli ponowna zamiana fragmentów podrzędnych z różnymi animacjami).
źródło
mNextAnim
znajduje się teraz wewnątrzmAnimationInfo
obiektu. Możesz uzyskać do niego dostęp w następujący sposób:Field animInfoField = Fragment.class.getDeclaredField("mAnimationInfo");
animInfoField.setAccessible(true);
Object animationInfo = animInfoField.get(fragment);
Field nextAnimField = animationInfo.getClass().getDeclaredField("mNextAnim");
val nextAnimResource = nextAnimField.getInt(animationInfo);
aby zastąpić linięint nextAnimResource = nextAnimField.getInt(fragment);
Udało mi się wymyślić całkiem czyste rozwiązanie. IMO jest najmniej hacky i chociaż technicznie jest to rozwiązanie „rysuj bitmapę”, przynajmniej jest to abstrakcyjne przez fragment lib.
Upewnij się, że fragi Twojego dziecka zastępują klasę nadrzędną tym:
Jeśli mamy animację wyjścia na fragmencie dzieci, będą one animowane zamiast mrugać. Możemy to wykorzystać, mając animację, która po prostu rysuje fragmenty potomne w pełnej alfa przez pewien czas. W ten sposób pozostaną widoczne we fragmencie nadrzędnym podczas jego animacji, zapewniając pożądane zachowanie.
Jedyny problem, jaki przychodzi mi do głowy, to śledzenie tego czasu trwania. Może mógłbym ustawić to na dużą liczbę, ale obawiam się, że może to mieć problemy z wydajnością, jeśli nadal gdzieś rysuje tę animację.
źródło
Dla jasności zamieszczam moje rozwiązanie. Rozwiązanie jest dość proste. Jeśli próbujesz naśladować animację transakcji fragmentu rodzica, po prostu dodaj niestandardową animację do transakcji fragmentu potomnego o tym samym czasie trwania. Aha i upewnij się, że ustawiłeś niestandardową animację przed add ().
XML dla R.anim.none (czas animacji wejścia / wyjścia moich rodziców wynosi 250 ms)
źródło
fragmentTransaction.setCustomAnimations(R.anim.none, 0, R.anim.none, R.anim.none)
Rozumiem, że to może nie być w stanie całkowicie rozwiązać twojego problemu, ale może będzie to odpowiadało potrzebom kogoś innego, możesz dodać
enter
/exit
ipopEnter
/popExit
animacje do swoich dzieciFragment
, które tak naprawdę nie poruszają / animująFragment
. Dopóki animacje mają taki sam czas trwania / przesunięcie jak ichFragment
animacje nadrzędne , będą wyglądać na poruszające / animowane wraz z animacją rodzica.źródło
możesz to zrobić we fragmencie podrzędnym.
źródło
@@@@@@@@@@@@@@@@@@@@@@@@@@
EDYCJA: Skończyło się na tym, że nie zaimplementowałem tego rozwiązania, ponieważ były inne problemy, które ma. Niedawno wyszedł Square z 2 bibliotekami, które zastępują fragmenty. Powiedziałbym, że może to być lepsza alternatywa niż próba włamania fragmentów do zrobienia czegoś, czego Google nie chce, aby robili.
http://corner.squareup.com/2014/01/mortar-and-flow.html
@@@@@@@@@@@@@@@@@@@@@@@@@@
Pomyślałem, że zaproponuję to rozwiązanie, aby pomóc ludziom, którzy mają ten problem w przyszłości. Jeśli prześledzisz oryginalną rozmowę plakatów z innymi osobami i spojrzysz na opublikowany przez niego kod, zobaczysz, że oryginalny plakat ostatecznie kończy się użyciem animacji bez operacji na fragmentach podrzędnych podczas animowania fragmentu nadrzędnego. To rozwiązanie nie jest idealne, ponieważ zmusza Cię do śledzenia wszystkich fragmentów podrzędnych, co może być uciążliwe podczas korzystania z ViewPager z FragmentPagerAdapter.
Ponieważ wszędzie używam Child Fragments, wymyśliłem to rozwiązanie, które jest wydajne i modułowe (dzięki czemu można je łatwo usunąć) na wypadek, gdyby kiedykolwiek to naprawili, a ta animacja bez operacji nie jest już potrzebna.
Można to wdrożyć na wiele sposobów. Zdecydowałem się użyć singletona i nazywam go ChildFragmentAnimationManager. Zasadniczo będzie śledzić dla mnie fragment podrzędny na podstawie jego rodzica i zastosuje animację bez operacji do dzieci, gdy zostanie o to poproszony.
Następnie musisz mieć klasę, która rozszerza Fragment, który rozszerza wszystkie twoje fragmenty (przynajmniej twoje fragmenty potomne). Miałem już tę klasę i nazywam ją BaseFragment. Po utworzeniu widoku fragmentów dodajemy go do ChildFragmentAnimationManager i usuwamy go po zniszczeniu. Możesz to zrobić na Attach / Detach lub w innych dopasowanych metodach w sekwencji. Moja logika wyboru widoku Utwórz / zniszcz widok była taka, że jeśli fragment nie ma widoku, nie obchodzi mnie animowanie go, aby nadal był widoczny. To podejście powinno również działać lepiej z ViewPagerami, które używają fragmentów, ponieważ nie będziesz śledzić każdego pojedynczego fragmentu, który przechowuje FragmentPagerAdapter, ale tylko 3.
Teraz, gdy wszystkie twoje fragmenty są przechowywane w pamięci przez fragment nadrzędny, możesz wywołać na nich animate w ten sposób, a twoje fragmenty podrzędne nie znikną.
Poza tym, tak to masz, oto plik no_anim.xml, który znajduje się w folderze res / anim:
Ponownie, nie sądzę, aby to rozwiązanie było idealne, ale jest znacznie lepsze niż w przypadku każdego wystąpienia fragmentu potomnego, implementującego niestandardowy kod w fragmencie nadrzędnym, aby śledzić każde dziecko. Byłem tam i to nie jest zabawne.
źródło
Myślę, że znalazłem lepsze rozwiązanie tego problemu niż zrobienie migawki bieżącego fragmentu do mapy bitowej, jak sugerował Luksprog.
Sztuczka polega na tym, żeby się ukryć usuwanego lub odłączanego fragmentu i dopiero po zakończeniu animacji fragment jest usuwany lub odłączany we własnej transakcji fragmentowej.
Wyobraź sobie, że mamy
FragmentA
iFragmentB
, oba z sub-fragmentami. Teraz, kiedy normalnie byś to zrobił:Zamiast tego robisz
Teraz do wdrożenia fragmentu:
źródło
Miałem ten sam problem z fragmentem mapy. Znikał podczas animacji wyjścia zawierającego go fragmentu. Sposób obejścia problemu polega na dodaniu animacji dla fragmentu mapy podrzędnej, która będzie widoczna podczas animacji wyjścia z fragmentu nadrzędnego. Animacja fragmentu podrzędnego zachowuje jego alfa na 100% przez cały czas trwania.
Animacja: res / animator / keep_child_fragment.xml
Animacja jest następnie stosowana, gdy fragment mapy jest dodawany do fragmentu nadrzędnego.
Fragment nadrzędny
Na koniec czas trwania animacji fragmentu potomnego jest ustawiany w pliku zasobów.
wartości / integers.xml
źródło
Aby ożywić znikanie usuniętych fragmentów, możemy wymusić stos pop back na ChildFragmentManager. Spowoduje to uruchomienie animacji przejścia. Aby to zrobić, musimy nadrobić zaległości w zdarzeniu OnBackButtonPressed lub nasłuchiwać zmian wstecz.
Oto przykład z kodem.
źródło
Niedawno napotkałem ten problem w moim pytaniu: Zagnieżdżone fragmenty przechodzą nieprawidłowo
Mam rozwiązanie, które rozwiązuje ten problem bez zapisywania bitmapy, stosowania odbić lub innych niezadowalających metod.
Przykładowy projekt można obejrzeć tutaj: https://github.com/zafrani/NestedFragmentTransitions
Efekt GIF można obejrzeć tutaj: https://imgur.com/94AvrW4
W moim przykładzie jest 6 fragmentów potomnych podzielonych na dwa fragmenty rodzicielskie. Jestem w stanie osiągnąć przejścia dla wejścia, wyjścia, pop i pchania bez żadnych problemów. Również zmiany konfiguracji i prasy wsteczne są z powodzeniem obsługiwane.
Większość rozwiązania znajduje się w mojej funkcji BaseFragment (fragment rozszerzony przez moje elementy podrzędne i nadrzędne) onCreateAnimator, która wygląda następująco:
Aktywność i fragment macierzysty są odpowiedzialne za ustawienie stanów tych wartości logicznych. Łatwiej jest zobaczyć, jak i gdzie z mojego przykładowego projektu.
W moim przykładzie nie używam fragmentów wsparcia, ale ta sama logika może być używana z nimi i ich funkcją onCreateAnimation
źródło
Prostym sposobem rozwiązania tego problemu jest użycie
Fragment
klasy z tej biblioteki zamiast standardowej klasy fragmentu biblioteki:https://github.com/marksalpeter/contract-fragment
Na marginesie, pakiet zawiera również przydatny wzorzec delegata o nazwie
ContractFragment
, który może być przydatny do tworzenia aplikacji wykorzystujących relację fragment nadrzędny-podrzędny.źródło
Z powyższej odpowiedzi @kcoppock,
jeśli masz Aktywność-> Fragment-> Fragmenty (wielokrotne układanie, poniższe pomaga), drobna edycja najlepszej odpowiedzi IMHO.
źródło
Mój problem polegał na usunięciu fragmentu nadrzędnego (ft.remove (fragment)), animacje potomne nie działały.
Podstawowym problemem jest to, że fragmenty potomne są natychmiast ZNISZCZONE PRZED wyjściem z animacji fragmentu rodziców.
Niestandardowe animacje fragmentów podrzędnych nie są wykonywane po usunięciu fragmentu nadrzędnego
Jak inni uniknęli, ukrycie RODZICA (a nie dziecka) przed usunięciem RODZICA jest drogą do zrobienia.
Jeśli faktycznie chcesz usunąć rodzica, prawdopodobnie powinieneś ustawić nasłuchiwanie na swojej niestandardowej animacji, aby wiedzieć, kiedy animacja się zakończy, więc możesz bezpiecznie przeprowadzić finalizację na fragmencie nadrzędnym (usuń). Jeśli tego nie zrobisz, w odpowiednim czasie, możesz skończyć z zabiciem animacji. Animacja NB jest wykonywana na własnej asynchronicznej kolejce.
BTW nie potrzebujesz niestandardowych animacji na fragmencie podrzędnym, ponieważ odziedziczą one animacje nadrzędne.
źródło
Problem został rozwiązany w
androidx.fragment:fragment:1.2.0-alpha02
. Więcej informacji znajdziesz na https://issuetracker.google.com/issues/116675313 .źródło
Stary wątek, ale na wypadek, gdyby ktoś się tu potknął:
Wszystkie powyższe podejścia są dla mnie bardzo nieatrakcyjne, rozwiązanie bitmapowe jest bardzo brudne i nie działa; inne wymagają, aby fragmenty potomne wiedziały o czasie trwania przejścia używanego w transakcji używanej do utworzenia danego fragmentu podrzędnego. Lepszym rozwiązaniem w moich oczach jest coś takiego:
Po prostu ukrywamy aktualny fragment i dodajemy nowy fragment, po zakończeniu animacji usuwamy stary fragment. W ten sposób jest obsługiwany w jednym miejscu i nie jest tworzona żadna bitmapa.
źródło