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:
tab 1
screen 1 -> screen 2 -> screen 3
tab 2
screen 4
tab 3
screen 5 -> 6
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:
MainTabActivity
ActivityInTab1
Fragment1 -> Fragment2 -> Fragment3
ActivityInTab2
Fragment4
ActivityInTab3
Fragment5 -> Fragment6
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:
abstract class ActivityInTab extends FragmentActivity { // FragmentActivity is just Activity for the support library.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_in_tab);
}
/**
* Navigates to a new fragment, which is added in the fragment container
* view.
*
* @param newFragment
*/
protected void navigateTo(Fragment newFragment) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, newFragment);
// Add this transaction to the back stack, so when the user presses back,
// it rollbacks.
ft.addToBackStack(null);
ft.commit();
}
}
Die activity_in_tab.xml ist genau das:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:isScrollContainer="true">
</RelativeLayout>
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:
// In ActivityInTab.java...
@Override
public void onBackPressed() {
FragmentManager manager = getSupportFragmentManager();
if (manager.getBackStackEntryCount() > 0) {
// If there are back-stack entries, leave the FragmentActivity
// implementation take care of them.
super.onBackPressed();
} else {
// Otherwise, ask user if he wants to leave :)
showExitDialog();
}
}
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):
public class ActivityInTab1 extends ActivityInTab {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
navigateTo(new Fragment1());
}
}
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:
// In Fragment1.java for example...
// Need to navigate to Fragment2.
((ActivityIntab) getActivity()).navigateTo(new Fragment2());
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.