Próbuję zaimplementować karty do nawigacji w aplikacji na Androida. Ponieważ TabActivity i ActivityGroup są przestarzałe, chciałbym zaimplementować je za pomocą fragmentów.
Wiem, jak ustawić jeden fragment dla każdej karty, a następnie przełączać fragmenty po kliknięciu karty. Ale jak mogę mieć osobny stos tylny dla każdej karty?
Na przykład fragmenty A i B będą znajdować się na karcie 1, a fragment C i D w karcie 2. Po uruchomieniu aplikacji wyświetlany jest fragment A i wybrana jest karta 1. Następnie Fragment A można zastąpić fragmentem B. Po wybraniu zakładki 2 Fragment C powinien zostać wyświetlony. Jeśli zostanie wybrana zakładka 1, fragment B powinien zostać ponownie wyświetlony. W tym momencie powinno być możliwe użycie przycisku Wstecz, aby wyświetlić Fragment A.
Ponadto ważne jest, aby stan każdej klapki był zachowany, gdy urządzenie jest obracane.
BR Martin
źródło
Strasznie spóźniam się na to pytanie. Ale ponieważ ten wątek był dla mnie bardzo pouczający i pomocny, pomyślałem, że lepiej opublikuję tutaj moje dwa pensy.
Potrzebowałem takiego przepływu ekranu (minimalistyczny projekt z 2 zakładkami i 2 widokami w każdej zakładce),
W przeszłości miałem te same wymagania i robiłem to przy użyciu
TabActivityGroup
(co było wówczas przestarzałe) i Aktywności. Tym razem chciałem użyć Fragmentów.Więc tak to zrobiłem.
1. Utwórz podstawową klasę fragmentów
Wszystkie fragmenty w Twojej aplikacji mogą rozszerzać tę klasę bazową. Jeśli chcesz używać specjalnych fragmentów, takich jak
ListFragment
powinieneś utworzyć klasę bazową również do tego. Będziesz mieć jasność co do użyciaonBackPressed()
ionActivityResult()
przeczytania całego posta.2. Utwórz identyfikatory zakładek, dostępne w całym projekcie
nie ma tu nic do wyjaśnienia ..
3. OK, Aktywność na karcie głównej - przejrzyj komentarze w kodzie.
4. app_main_tab_fragment_layout.xml (w przypadku zainteresowania.)
5. AppTabAFirstFragment.java (pierwszy fragment w Tab A, podobny do wszystkich zakładek)
To może nie być najbardziej dopracowany i poprawny sposób. Ale w moim przypadku zadziałało pięknie. Miałem ten wymóg tylko w trybie portretowym. Nigdy nie musiałem używać tego kodu w projekcie wspierającym obie orientacje. Nie mogę więc powiedzieć, jakie wyzwania tam stoję.
EDYTOWAĆ :
Jeśli ktoś chce mieć pełny projekt, umieściłem przykładowy projekt na github .
źródło
Musieliśmy zaimplementować dokładnie to samo zachowanie, które ostatnio opisujesz dla aplikacji. Ekrany i ogólny przebieg aplikacji zostały już zdefiniowane, więc musieliśmy się tego trzymać (to klon aplikacji na iOS ...). Na szczęście udało nam się pozbyć ekranowych przycisków wstecz :)
Zhakowaliśmy rozwiązanie przy użyciu mieszanki TabActivity, FragmentActivities (używaliśmy biblioteki obsługi fragmentów) i Fragments. Z perspektywy czasu jestem prawie pewien, że nie była to najlepsza decyzja dotycząca architektury, ale udało nam się to naprawić. Gdybym musiał to zrobić ponownie, prawdopodobnie spróbuję zrobić rozwiązanie bardziej oparte na aktywności (bez fragmentów) lub spróbować mieć tylko jedną aktywność dla zakładek i niech cała reszta będzie widokami (które uważam za znacznie więcej wielokrotnego użytku niż ogólnie czynności).
Tak więc wymagania były takie, aby w każdej zakładce było kilka zakładek i ekranów z możliwością zagnieżdżania:
itp...
Powiedzmy: użytkownik uruchamia się na karcie 1, przechodzi od ekranu 1 do ekranu 2, a następnie do ekranu 3, a następnie przechodzi do karty 3 i przechodzi od ekranu 4 do 6; jeśli przełączył się z powrotem na kartę 1, powinien ponownie zobaczyć ekran 3, a jeśli nacisnął przycisk Wstecz, powinien wrócić do ekranu 2; Znowu i jest na ekranie 1; przejdź do zakładki 3 i znowu jest na ekranie 6.
Głównym działaniem w aplikacji jest MainTabActivity, które rozszerza TabActivity. Każda zakładka jest powiązana z działaniem, powiedzmy ActivityInTab1, 2 i 3. A potem każdy ekran będzie fragmentem:
Każda karta ActivityInTab zawiera tylko jeden fragment na raz i wie, jak zastąpić jeden fragment innym (prawie tak samo jak ActvityGroup). Fajne jest to, że w ten sposób dość łatwo jest utrzymać oddzielne stosy tylne dla każdej karty.
Funkcjonalność każdej karty ActivityInTab była taka sama: umieć nawigować z jednego fragmentu do drugiego i utrzymywać stos, więc umieściliśmy go w klasie bazowej. Nazwijmy to po prostu ActivityInTab:
Activity_in_tab.xml jest po prostu taka:
Jak widać, układ widoku dla każdej karty był taki sam. Dzieje się tak, ponieważ jest to tylko FrameLayout o nazwie content, który będzie zawierał każdy fragment. Fragmenty to te, które mają widok każdego ekranu.
Tylko dla punktów bonusowych dodaliśmy również mały kod, aby wyświetlić okno dialogowe potwierdzenia, gdy użytkownik naciśnie przycisk Wstecz i nie ma więcej fragmentów, do których można wrócić:
To prawie konfiguracja. Jak widać, każda FragmentActivity (lub po prostu aktywność w systemie Android> 3) dba o całe układanie w stos z własnym FragmentManager.
Działanie takie jak ActivityInTab1 będzie naprawdę proste, pokaże tylko swój pierwszy fragment (tj. Ekran):
Następnie, jeśli fragment musi przejść do innego fragmentu, musi wykonać trochę nieprzyjemnego rzucania ... ale nie jest tak źle:
Więc to wszystko. Jestem prawie pewien, że nie jest to bardzo kanoniczne (i na pewno niezbyt dobre) rozwiązanie, więc chciałbym zapytać doświadczonych programistów Androida, jakie byłoby lepsze podejście do uzyskania tej funkcjonalności, a jeśli nie jest to „jak to jest gotowe ”w Androidzie, byłbym wdzięczny, gdybyś wskazał mi jakiś link lub materiał wyjaśniający, w jaki sposób można to zrobić w systemie Android (karty, zagnieżdżone ekrany w kartach itp.). Zapraszam do rozdzielenia tej odpowiedzi w komentarzach :)
Znakiem, że to rozwiązanie nie jest zbyt dobre, jest to, że ostatnio musiałem dodać do aplikacji jakąś funkcjonalność nawigacyjną. Jakiś dziwaczny przycisk, który powinien przenosić użytkownika z jednej karty do drugiej i do zagnieżdżonego ekranu. Robienie tego programowo było uciążliwe, ponieważ problemy „kto-wie-kto” i radzenie sobie z tym, kiedy są fragmentami i działaniami, które zostały faktycznie zainicjowane i zainicjowane. Myślę, że byłoby znacznie łatwiej, gdyby te ekrany i karty były tak naprawdę tylko widokami.
Na koniec, jeśli chcesz przetrwać zmiany orientacji, ważne jest, aby Twoje fragmenty były tworzone za pomocą setArguments / getArguments. Jeśli ustawisz zmienne instancji w konstruktorach twoich fragmentów, będziesz spieprzony. Ale na szczęście to naprawdę łatwe do naprawienia: po prostu zapisz wszystko w setArguments w konstruktorze, a następnie pobierz te rzeczy za pomocą getArguments w onCreate, aby z nich skorzystać.
źródło
Można to łatwo osiągnąć za pomocą ChildFragmentManager
Oto post na ten temat z powiązanym projektem. Spójrz,
http://tausiq.wordpress.com/2014/06/06/android-multiple-fragments-stack-in-each-viewpager-tab/
źródło
Przechowywanie silnych odniesień do fragmentów nie jest właściwym sposobem.
FragmentManager zapewnia
putFragment(Bundle, String, Fragment)
isaveFragmentInstanceState(Fragment)
.Aby zaimplementować backstack, wystarczy jeden.
Używając
putFragment
zamiast zastępowania fragmentu, odłączasz stary i dodajesz nowy. To właśnie robi framework z transakcją zamiany, która jest dodawana do backstack.putFragment
przechowuje indeks do bieżącej listy aktywnych fragmentów, a te fragmenty są zapisywane przez strukturę podczas zmian orientacji.Drugi sposób, za pomocą
saveFragmentInstanceState
, zapisuje stan całego fragmentu w paczce, co pozwala ci go naprawdę usunąć, zamiast odłączać. Takie podejście ułatwia manipulowanie tylnym stosem, ponieważ możesz wrzucić fragment, kiedy tylko chcesz.Użyłem drugiej metody w tym przypadku:
Nie chcę, aby użytkownik wracał do ekranu rejestracji, z trzeciego, naciskając przycisk Wstecz. Wykonuję również animacje między nimi (używając
onCreateAnimation
), więc hackerskie rozwiązania nie zadziałają, przynajmniej bez wyraźnego zauważenia przez użytkownika, że coś jest nie tak.To jest prawidłowy przypadek użycia niestandardowego backstacka, robiącego to, czego oczekuje użytkownik ...
źródło
Zrzeczenie się:
Uważam, że jest to najlepsze miejsce, aby opublikować podobne rozwiązanie, nad którym pracowałem, w przypadku podobnego problemu, który wydaje się być dość standardowym problemem dla Androida. To nie rozwiąże problemu dla wszystkich, ale może niektórym pomóc.
Jeśli podstawową różnicą między twoimi fragmentami są tylko dane, z których są wykonane kopie zapasowe (tj. Nie ma wielu dużych różnic w układzie), może nie być konieczne zastępowanie fragmentu, a jedynie zamiana podstawowych danych i odświeżenie widoku.
Oto opis jednego możliwego przykładu tego podejścia:
Mam aplikację, która używa ListViews. Każda pozycja na liście to rodzic z pewną liczbą dzieci. Po stuknięciu elementu nowa lista musi zostać otwarta z tymi elementami podrzędnymi na tej samej karcie ActionBar, co oryginalna lista. Te zagnieżdżone listy mają bardzo podobny układ (być może niektóre warunkowe poprawki tu i tam), ale dane są inne.
Ta aplikacja ma kilka warstw potomstwa pod początkową listą nadrzędną i możemy, ale nie muszą, mieć dane z serwera do czasu, gdy użytkownik spróbuje uzyskać dostęp do określonej głębokości poza pierwszą. Ponieważ lista jest zbudowana z kursora bazy danych, a fragmenty używają programu ładującego kursor i adaptera kursora do zapełniania widoku listy elementami listy, wszystko, co musi się wydarzyć po zarejestrowaniu kliknięcia, to:
1) Utwórz nowy adapter z odpowiednimi polami „do” i „z”, które będą pasować do nowych widoków pozycji dodawanych do listy i kolumn zwracanych przez nowy kursor.
2) Ustaw ten adapter jako nowy adapter dla ListView.
3) Zbuduj nowy URI na podstawie elementu, który został kliknięty i zrestartuj program ładujący kursor z nowym URI (i projekcją). W tym przykładzie identyfikator URI jest mapowany na określone zapytania z argumentami wyboru przekazywanymi z interfejsu użytkownika.
4) Po załadowaniu nowych danych z identyfikatora URI zamień kursor powiązany z adapterem na nowy kursor, a lista zostanie odświeżona.
Nie ma z tym powiązania, ponieważ nie używamy transakcji, więc będziesz musiał albo zbudować własne, albo odtwarzać zapytania w odwrotnej kolejności, gdy wycofujesz się z hierarchii. Kiedy próbowałem tego, zapytania były na tyle szybkie, że po prostu wykonałem je ponownie w onBackPressed (), aż znajdę się na szczycie hierarchii, w którym to momencie struktura ponownie przejmuje przycisk Wstecz.
Jeśli znajdziesz się w podobnej sytuacji, koniecznie przeczytaj dokumentację: http://developer.android.com/guide/topics/ui/layout/listview.html
http://developer.android.com/reference/android/support/v4/app/LoaderManager.LoaderCallbacks.html
Mam nadzieję, że to komuś pomoże!
źródło
Miałem dokładnie ten sam problem i zaimplementowałem projekt github o otwartym kodzie źródłowym, który obejmuje ułożone w stosy karty, nawigację po kopii zapasowej i wstecz oraz jest dobrze przetestowany i udokumentowany:
https://github.com/SebastianBaltesObjectCode/PersistentFragmentTabs
źródło
Jest to złożony problem, ponieważ Android obsługuje tylko 1 stos tylny, ale jest to wykonalne. Stworzenie biblioteki o nazwie Tab Stacker zajęło mi kilka dni, która robi dokładnie to, czego szukasz: historię fragmentów dla każdej karty. Jest open source, w pełni udokumentowany i można go łatwo dołączyć do gradle. Bibliotekę można znaleźć na github: https://github.com/smart-fun/TabStacker
Możesz również pobrać przykładową aplikację, aby sprawdzić, czy zachowanie odpowiada Twoim potrzebom:
https://play.google.com/apps/testing/fr.arnaudguyon.tabstackerapp
Jeśli masz jakieś pytanie, nie wahaj się i napisz.
źródło
Chciałbym zaproponować własne rozwiązanie na wypadek, gdyby ktoś szukał i chciałby spróbować wybrać najlepsze dla swoich potrzeb.
https://github.com/drusak/tabactivity
Cel tworzenia biblioteki jest dość banalny - zaimplementuj ją jak iPhone'a.
Główne zalety:
źródło
ListFragment
s opróczFragment
s, więc zduplikowałem BaseTabFragment.java do BaseTabListFragment.java i kazałem mu rozszerzyć ListFragment. Następnie musiałem zmienić różne części w kodzie, w których zawsze zakładano, że oczekuję BaseTabFragment. Czy jest lepszy sposób?Proste rozwiązanie:
Za każdym razem, gdy zmieniasz wywołanie widoku karty / katalogu głównego:
To wyczyści BackStack. Pamiętaj, aby wywołać to przed zmianą fragmentu głównego.
I dodaj fragmenty z tym:
Zauważ, że
.addToBackStack(null)
itransaction.add
można np. Zmienić za pomocątransaction.replace
.źródło
Ten wątek był bardzo interesujący i przydatny.
Dzięki Krishnabhadra za wyjaśnienie i kod, używam twojego kodu i poprawiłem go trochę, pozwalając zachować stosy, currentTab, itp ... ze zmiany konfiguracji (głównie rotacji).
Testowane na prawdziwych urządzeniach 4.0.4 i 2.3.6, nie testowane na emulatorze
Zmieniam tę część kodu na „AppMainTabActivity.java”, reszta pozostaje taka sama. Może Krysznabhadra doda to do swojego kodu.
Odzyskaj dane onCreate:
Zapisz zmienne i umieść w pakiecie:
Jeśli istnieje poprzednia CurrentTab, ustaw ją, w przeciwnym razie utwórz nową Tab_A:
Mam nadzieję, że to pomoże innym ludziom.
źródło
Zalecałbym nie używać backstacka opartego na HashMap> jest wiele błędów w trybie „nie zachowuj aktywności”. Nie przywróci poprawnie stanu w przypadku, gdy jesteś głęboko w stosie fragmentów. A także zostanie zgnieciony w zagnieżdżonym fragmencie mapy (z wyjątkiem: fragmentu nie znaleziono widoku dla ID). Coz HashMap> po aplikacji w tle \ na pierwszym planie będzie miała wartość NULL
Powyższy kod optymalizuję pod kątem pracy z backstackiem fragmentu
To jest dolny TabView
Główna działalność Klasa
tags_activity.xml
<
Tags_icon.xml
źródło