Ich versuche, Registerkarten für die Navigation in einer Android-App zu implementieren. Da TabActivity und ActivityGroup veraltet sind, möchte ich es stattdessen mit Fragmenten implementieren.
Ich weiß, wie man ein Fragment für jede Registerkarte einrichtet und dann die Fragmente wechselt, wenn auf eine Registerkarte geklickt wird. Aber wie kann ich für jede Registerkarte einen eigenen Backstack haben?
In einem Beispiel befinden sich Fragment A und B unter Tab 1 und Fragment C und D unter Tab 2. Wenn die App gestartet wird, wird Fragment A angezeigt und Tab 1 ausgewählt. Dann könnte Fragment A durch Fragment B ersetzt werden. Wenn Tab 2 ausgewählt ist, sollte Fragment C angezeigt werden. Wenn Tab 1 ausgewählt ist, sollte Fragment B erneut angezeigt werden. Zu diesem Zeitpunkt sollte es möglich sein, mit der Schaltfläche "Zurück" Fragment A anzuzeigen.
Es ist auch wichtig, dass der Status für jede Registerkarte beibehalten wird, wenn das Gerät gedreht wird.
BR Martin
quelle
Ich bin furchtbar spät bei dieser Frage. Aber da dieser Thread für mich sehr informativ und hilfreich war, dachte ich, ich poste meine zwei Pence besser hier.
Ich brauchte einen solchen Bildschirmablauf (ein minimalistisches Design mit 2 Registerkarten und 2 Ansichten in jeder Registerkarte).
Ich hatte in der Vergangenheit die gleichen Anforderungen und habe dies mit
TabActivityGroup
(was zu diesem Zeitpunkt ebenfalls veraltet war) und Aktivitäten durchgeführt. Diesmal wollte ich Fragmente verwenden.So habe ich es gemacht.
1. Erstellen Sie eine Basisfragmentklasse
Alle Fragmente in Ihrer App können diese Basisklasse erweitern. Wenn Sie spezielle Fragmente wie verwenden möchten,
ListFragment
sollten Sie auch dafür eine Basisklasse erstellen. Sie werden über die Verwendung von klar seinonBackPressed()
undonActivityResult()
wenn Sie den Beitrag vollständig lesen ..2. Erstellen Sie einige Registerkarten-IDs, auf die überall im Projekt zugegriffen werden kann
hier gibt es nichts zu erklären ..
3. Ok, Hauptregisteraktivität - Bitte gehen Sie die Kommentare im Code durch.
4. app_main_tab_fragment_layout.xml (Falls jemand interessiert ist.)
5. AppTabAFirstFragment.java (Erstes Fragment in Tab A, ähnlich für alle Tabs)
Dies ist möglicherweise nicht der polierteste und korrekteste Weg. Aber in meinem Fall hat es wunderbar funktioniert. Auch hatte ich diese Anforderung nur im Hochformat. Ich musste diesen Code nie in einem Projekt verwenden, das beide Orientierungen unterstützt. Ich kann also nicht sagen, welchen Herausforderungen ich dort gegenüberstehe.
EDIT:
Wenn jemand ein vollständiges Projekt haben möchte, habe ich ein Beispielprojekt an github gesendet .
quelle
Wir mussten genau das Verhalten implementieren, das Sie kürzlich für eine App beschrieben haben. Die Bildschirme und der Gesamtfluss der Anwendung wurden bereits definiert, sodass wir uns daran halten mussten (es ist ein iOS-App-Klon ...). Zum Glück haben wir es geschafft, die Zurück-Schaltflächen auf dem Bildschirm loszuwerden :)
Wir haben die Lösung mit einer Mischung aus TabActivity, FragmentActivities (wir haben die Support-Bibliothek für Fragmente verwendet) und Fragmenten gehackt. Rückblickend bin ich mir ziemlich sicher, dass es nicht die beste Architekturentscheidung war, aber wir haben es geschafft, das Ding zum Laufen zu bringen. Wenn ich es noch einmal tun müsste, würde ich wahrscheinlich versuchen, eine aktivitätsbasierte Lösung (keine Fragmente) zu erstellen, oder versuchen, nur eine Aktivität für die Registerkarten zu haben und alle anderen Ansichten sein zu lassen (was meiner Meinung nach viel mehr ist wiederverwendbar als Aktivitäten insgesamt).
Die Anforderungen waren also, dass in jeder Registerkarte einige Registerkarten und verschachtelbare Bildschirme vorhanden waren:
etc...
Sagen wir also: Der Benutzer startet in Tab 1, navigiert von Bildschirm 1 zu Bildschirm 2 und dann zu Bildschirm 3, wechselt dann zu Tab 3 und navigiert von Bildschirm 4 zu 6; Wenn er zurück zu Tab 1 wechselt, sollte er Bildschirm 3 wieder sehen und wenn er Zurück drückt, sollte er zu Bildschirm 2 zurückkehren. Wieder zurück und er ist in Bildschirm 1; Wechseln Sie zu Tab 3 und er ist wieder in Bildschirm 6.
Die Hauptaktivität in der Anwendung ist MainTabActivity, wodurch TabActivity erweitert wird. Jede Registerkarte ist einer Aktivität zugeordnet, beispielsweise ActivityInTab1, 2 und 3. Und dann ist jeder Bildschirm ein Fragment:
Jedes ActivityInTab enthält jeweils nur ein Fragment und weiß, wie ein Fragment durch ein anderes ersetzt wird (fast das gleiche wie bei einer ActvityGroup). Das Coole ist, dass es auf diese Weise ziemlich einfach ist, separate Rückenstapel für jede Lasche zu erhalten.
Die Funktionalität für jedes ActivityInTab war ziemlich gleich: Wissen, wie man von einem Fragment zum anderen navigiert und einen Backstack verwaltet, also haben wir das in eine Basisklasse eingefügt. Nennen wir es einfach ActivityInTab:
Die activity_in_tab.xml ist genau das:
Wie Sie sehen können, war das Ansichtslayout für jede Registerkarte das gleiche. Das liegt daran, dass es sich nur um ein FrameLayout namens content handelt, das jedes Fragment enthält. Die Fragmente sind diejenigen, die die Ansicht jedes Bildschirms haben.
Nur für die Bonuspunkte haben wir auch einen kleinen Code hinzugefügt, um einen Bestätigungsdialog anzuzeigen, wenn der Benutzer Zurück drückt und es keine Fragmente mehr gibt, zu denen er zurückkehren kann:
Das ist so ziemlich das Setup. Wie Sie sehen können, kümmert sich jede FragmentActivity (oder einfach nur Aktivität in Android> 3) um das gesamte Backstacking mit einem eigenen FragmentManager.
Eine Aktivität wie ActivityInTab1 ist sehr einfach und zeigt nur das erste Fragment (dh den Bildschirm):
Wenn ein Fragment dann zu einem anderen Fragment navigieren muss, muss es ein wenig böses Casting durchführen ... aber es ist nicht so schlimm:
Das war's auch schon. Ich bin mir ziemlich sicher, dass dies keine sehr kanonische (und meistens sicher nicht sehr gute) Lösung ist. Daher möchte ich erfahrene Android-Entwickler fragen, was ein besserer Ansatz wäre, um diese Funktionalität zu erreichen, und ob dies nicht der Fall ist done“in Android, würde ich schätzen , wenn Sie mich zu einem gewissen Link oder Material zeigen könnten, der erklärt , was die Android Art und Weise diesen (Tabs, verschachtelte Bildschirme in Registerkarten usw.) zu nähern. Fühlen Sie sich frei, diese Antwort in den Kommentaren auseinander zu reißen :)
Als Zeichen dafür, dass diese Lösung nicht sehr gut ist, musste ich der Anwendung kürzlich einige Navigationsfunktionen hinzufügen. Eine bizarre Schaltfläche, die den Benutzer von einer Registerkarte in eine andere und in einen verschachtelten Bildschirm führen soll. Das programmgesteuert zu tun war ein Schmerz im Hintern, weil wer-weiß-wer-Probleme und der Umgang mit wann Fragmente und Aktivitäten tatsächlich instanziiert und initialisiert werden. Ich denke, es wäre viel einfacher gewesen, wenn diese Bildschirme und Registerkarten wirklich nur Ansichten gewesen wären.
Wenn Sie Orientierungsänderungen überleben müssen, ist es wichtig, dass Ihre Fragmente mit setArguments / getArguments erstellt werden. Wenn Sie Instanzvariablen in den Konstruktoren Ihrer Fragmente festlegen, werden Sie geschraubt. Aber zum Glück ist das wirklich einfach zu beheben: Speichern Sie einfach alles in setArguments im Konstruktor und rufen Sie diese Dinge dann mit getArguments in onCreate ab, um sie zu verwenden.
quelle
Dies kann leicht mit ChildFragmentManager erreicht werden
Hier ist ein Beitrag dazu mit zugehörigem Projekt. Schau mal,
http://tausiq.wordpress.com/2014/06/06/android-multiple-fragments-stack-in-each-viewpager-tab/
quelle
Das Speichern starker Verweise auf Fragmente ist nicht der richtige Weg.
FragmentManager bietet
putFragment(Bundle, String, Fragment)
undsaveFragmentInstanceState(Fragment)
.Beides reicht aus, um einen Backstack zu implementieren.
Unter Verwendung
putFragment
anstelle eines Fragment zu ersetzen, lösen Sie das alte und fügen Sie den neuen. Dies ist, was das Framework für eine Ersetzungstransaktion tut, die dem Backstack hinzugefügt wird.putFragment
speichert einen Index für die aktuelle Liste der aktiven Fragmente und diese Fragmente werden vom Framework bei Orientierungsänderungen gespeichert.Bei der zweiten Methode wird
saveFragmentInstanceState
der gesamte Fragmentstatus in einem Bundle gespeichert, sodass Sie ihn wirklich entfernen können, anstatt ihn zu trennen. Mit diesem Ansatz lässt sich der Backstack einfacher manipulieren, da Sie jederzeit ein Fragment einfügen können.Ich habe die zweite Methode für diesen Anwendungsfall verwendet:
Ich möchte nicht, dass der Benutzer vom dritten zum Anmeldebildschirm zurückkehrt, indem er die Zurück-Taste drückt. Ich mache auch Flip-Animationen zwischen ihnen (mit
onCreateAnimation
), so dass hackige Lösungen nicht funktionieren, zumindest ohne dass der Benutzer klar bemerkt, dass etwas nicht stimmt.Dies ist ein gültiger Anwendungsfall für einen benutzerdefinierten Backstack, der genau das tut, was der Benutzer erwartet ...
quelle
Haftungsausschluss:
Ich bin der Meinung, dass dies der beste Ort ist, um eine verwandte Lösung zu veröffentlichen, an der ich für eine ähnliche Art von Problem gearbeitet habe, die anscheinend ziemlich normal für Android ist. Es wird das Problem nicht für alle lösen, aber es kann einigen helfen.
Wenn der Hauptunterschied zwischen Ihren Fragmenten nur die Daten sind, die sie sichern (dh nicht viele große Layoutunterschiede), müssen Sie das Fragment möglicherweise nicht tatsächlich ersetzen, sondern lediglich die zugrunde liegenden Daten austauschen und die Ansicht aktualisieren.
Hier ist eine Beschreibung eines möglichen Beispiels für diesen Ansatz:
Ich habe eine App, die ListViews verwendet. Jedes Element in der Liste ist ein Elternteil mit einer bestimmten Anzahl von Kindern. Wenn Sie auf das Element tippen, muss eine neue Liste mit diesen untergeordneten Elementen auf derselben Registerkarte "Aktionsleiste" wie die ursprüngliche Liste geöffnet werden. Diese verschachtelten Listen haben ein sehr ähnliches Layout (einige bedingte Änderungen hier und da vielleicht), aber die Daten sind unterschiedlich.
Diese App hat mehrere Schichten von Nachkommen unterhalb der ursprünglichen Elternliste, und wir haben möglicherweise Daten vom Server, bis ein Benutzer versucht, auf eine bestimmte Tiefe über die erste hinaus zuzugreifen. Da die Liste aus einem Datenbankcursor erstellt wird und die Fragmente einen Cursorlader und einen Cursoradapter verwenden, um die Listenansicht mit Listenelementen zu füllen, müssen bei der Registrierung eines Klicks nur folgende Schritte ausgeführt werden:
1) Erstellen Sie einen neuen Adapter mit den entsprechenden Feldern "bis" und "von", die mit den neuen Elementansichten übereinstimmen, die der Liste hinzugefügt werden, sowie mit den vom neuen Cursor zurückgegebenen Spalten.
2) Legen Sie diesen Adapter als neuen Adapter für die ListView fest.
3) Erstellen Sie einen neuen URI basierend auf dem angeklickten Element und starten Sie den Cursorlader mit dem neuen URI (und der neuen Projektion) neu. In diesem Beispiel wird der URI bestimmten Abfragen zugeordnet, wobei die Auswahlargumente von der Benutzeroberfläche übergeben werden.
4) Wenn die neuen Daten aus dem URI geladen wurden, tauschen Sie den dem Adapter zugeordneten Cursor gegen den neuen Cursor. Die Liste wird dann aktualisiert.
Damit ist kein Backstack verbunden, da wir keine Transaktionen verwenden. Sie müssen also entweder Ihre eigenen erstellen oder die Abfragen beim Zurücksetzen aus der Hierarchie in umgekehrter Reihenfolge abspielen. Als ich dies versuchte, waren die Abfragen schnell genug, dass ich sie in oNBackPressed () einfach erneut ausführe, bis ich an der Spitze der Hierarchie stehe. Dann übernimmt das Framework wieder die Schaltfläche "Zurück".
Wenn Sie sich in einer ähnlichen Situation befinden, lesen Sie unbedingt die folgenden Dokumente: http://developer.android.com/guide/topics/ui/layout/listview.html
http://developer.android.com/reference/android/support/v4/app/LoaderManager.LoaderCallbacks.html
Ich hoffe das hilft jemandem!
quelle
Ich hatte genau das gleiche Problem und implementierte ein Open-Source-Github-Projekt, das gestapelte Tabs, Back- und Up-Navigation abdeckt und gut getestet und dokumentiert ist:
https://github.com/SebastianBaltesObjectCode/PersistentFragmentTabs
quelle
Dies ist ein komplexes Problem, da Android nur 1 Backstack verarbeitet, dies ist jedoch möglich. Ich habe Tage gebraucht, um eine Bibliothek namens Tab Stacker zu erstellen, die genau das tut, wonach Sie suchen: einen Fragmentverlauf für jeden Tab. Es ist Open Source und vollständig dokumentiert und kann problemlos in gradle aufgenommen werden. Sie finden die Bibliothek auf github: https://github.com/smart-fun/TabStacker
Sie können auch die Beispiel-App herunterladen, um festzustellen, ob das Verhalten Ihren Anforderungen entspricht:
https://play.google.com/apps/testing/fr.arnaudguyon.tabstackerapp
Wenn Sie Fragen haben, zögern Sie nicht, eine Mail zu schreiben.
quelle
Ich möchte meine eigene Lösung vorschlagen, falls jemand auf der Suche ist und versuchen möchte, die beste für seine Bedürfnisse auszuwählen.
https://github.com/drusak/tabactivity
Der Zweck der Erstellung der Bibliothek ist ziemlich banal - implementieren Sie sie wie das iPhone.
Die Hauptvorteile:
quelle
ListFragment
s zusätzlich zuFragment
s verwenden, also habe ich BaseTabFragment.java in BaseTabListFragment.java dupliziert und ListFragment erweitern lassen. Dann musste ich verschiedene Teile im Code ändern, wobei immer davon ausgegangen wurde, dass ein BaseTabFragment erwartet wird. Gibt es einen besseren Weg?Eine einfache Lösung:
Jedes Mal, wenn Sie den Tab / Root-View-Aufruf ändern:
Der BackStack wird gelöscht. Denken Sie daran, dies aufzurufen, bevor Sie das Stammfragment ändern.
Und fügen Sie Fragmente hinzu:
Beachten Sie das
.addToBackStack(null)
und dastransaction.add
könnte zB mit geändert werdentransaction.replace
.quelle
Dieser Thread war sehr sehr interessant und nützlich.
Vielen Dank an Krishnabhadra für Ihre Erklärung und Ihren Code. Ich verwende Ihren Code und habe ihn ein wenig verbessert, damit die Stapel, das aktuelle Tab usw. von der Änderung der Konfiguration (hauptsächlich rotierend) erhalten bleiben.
Getestet auf einem echten 4.0.4 und 2.3.6 Gerät, nicht auf einem Emulator getestet
Ich ändere diesen Teil des Codes in "AppMainTabActivity.java", der Rest bleibt gleich. Vielleicht fügt Krishnabhadra dies seinem Code hinzu.
Daten wiederherstellen onCreate:
Speichern Sie die Variablen und legen Sie sie in Bundle:
Wenn ein vorheriges CurrentTab vorhanden ist, legen Sie dies fest, andernfalls erstellen Sie ein neues Tab_A:
Ich hoffe das hilft anderen Menschen.
quelle
Ich würde empfehlen, keinen Backstack zu verwenden, der auf HashMap basiert.> Es gibt viele Fehler im Modus "Aktivitäten nicht beibehalten". Der Status wird nicht korrekt wiederhergestellt, falls Sie sich tief im Fragmentstapel befinden. Und wird auch in verschachtelte Kartenfragmente eingeteilt (mit Ausnahme: Fragment keine Ansicht für ID gefunden). Coz HashMap> nach Hintergrund \ Vordergrund App ist null
Ich optimiere den obigen Code für die Arbeit mit dem Backstack des Fragments
Es ist unteres TabView
Hauptaktivitätsklasse
tags_activity.xml
<
tags_icon.xml
quelle