Kami harus menerapkan perilaku yang persis sama dengan yang Anda gambarkan untuk aplikasi baru-baru ini. Layar dan aliran keseluruhan aplikasi sudah ditentukan sehingga kami harus tetap menggunakannya (ini adalah tiruan aplikasi iOS ...). Untungnya, kami berhasil menyingkirkan tombol kembali di layar :)
Kami meretas solusi menggunakan campuran TabActivity, FragmentActivities (kami menggunakan perpustakaan dukungan untuk fragmen) dan Fragmen. Dalam retrospektif, saya cukup yakin itu bukan keputusan arsitektur terbaik, tetapi kami berhasil membuat hal itu berfungsi. Jika saya harus melakukannya lagi, saya mungkin akan mencoba untuk melakukan solusi berbasis aktivitas yang lebih (tidak ada fragmen), atau mencoba dan hanya memiliki satu Kegiatan untuk tab dan membiarkan semua yang lain dilihat (yang saya temukan jauh lebih banyak dapat digunakan kembali daripada aktivitas keseluruhan).
Jadi persyaratannya adalah memiliki beberapa tab dan layar yang bisa di-nestable di setiap tab:
tab 1
screen 1 -> screen 2 -> screen 3
tab 2
screen 4
tab 3
screen 5 -> 6
dll ...
Jadi katakan: pengguna mulai pada tab 1, menavigasi dari layar 1 ke layar 2 lalu ke layar 3, ia kemudian beralih ke tab 3 dan menavigasi dari layar 4 hingga 6; jika beralih kembali ke tab 1, ia harus melihat layar 3 lagi dan jika ia menekan Kembali ia harus kembali ke layar 2; Kembali lagi dan dia ada di layar 1; beralih ke tab 3 dan dia ada di layar 6 lagi.
Aktivitas utama dalam aplikasi adalah MainTabActivity, yang memperluas TabActivity. Setiap tab dikaitkan dengan suatu aktivitas, katakanlah ActivityInTab1, 2 dan 3. Dan kemudian setiap layar akan menjadi sebuah fragmen:
MainTabActivity
ActivityInTab1
Fragment1 -> Fragment2 -> Fragment3
ActivityInTab2
Fragment4
ActivityInTab3
Fragment5 -> Fragment6
Setiap ActivityInTab hanya memegang satu fragmen pada satu waktu, dan tahu bagaimana cara mengganti satu fragmen dengan yang lain (hampir sama dengan ActvityGroup). Yang keren adalah cukup mudah untuk menjaga tumpukan back terpisah untuk setiap tab dengan cara ini.
Fungsionalitas untuk setiap ActivityInTab cukup sama: tahu cara menavigasi dari satu fragmen ke fragmen lain dan mempertahankan tumpukan belakang, jadi kami menempatkannya di kelas dasar. Sebut saja 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();
}
}
Activity_in_tab.xml hanya ini:
<?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>
Seperti yang Anda lihat, tata letak tampilan untuk setiap tab adalah sama. Itu karena itu hanya konten yang disebut FrameLayout yang akan menampung setiap fragmen. Fragmen adalah yang memiliki tampilan layar masing-masing.
Hanya untuk poin bonus, kami juga menambahkan beberapa kode kecil untuk menampilkan dialog konfirmasi ketika pengguna menekan Kembali dan tidak ada lagi fragmen untuk kembali ke:
// 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();
}
}
Cukup banyak pengaturannya. Seperti yang Anda lihat, masing-masing FragmentActivity (atau hanya aktivitas di Android> 3) mengurus semua penumpukan kembali dengan FragmentManager itu sendiri.
Aktivitas seperti ActivityInTab1 akan sangat sederhana, itu hanya akan menunjukkan fragmen pertama (yaitu layar):
public class ActivityInTab1 extends ActivityInTab {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
navigateTo(new Fragment1());
}
}
Kemudian, jika sebuah fragmen perlu bernavigasi ke fragmen lain, ia harus melakukan casting yang tidak menyenangkan ... tapi itu tidak terlalu buruk:
// In Fragment1.java for example...
// Need to navigate to Fragment2.
((ActivityIntab) getActivity()).navigateTo(new Fragment2());
Jadi cukup banyak. Saya cukup yakin ini bukan solusi yang sangat kanonik (dan sebagian besar tentu tidak sangat baik), jadi saya ingin bertanya kepada pengembang Android berpengalaman apa yang akan menjadi pendekatan yang lebih baik untuk mencapai fungsi ini, dan jika ini bukan "bagaimana ini" dilakukan" di Android, saya akan menghargai jika Anda bisa mengarahkan saya ke beberapa link atau materi yang menjelaskan yang satu cara Android untuk pendekatan ini (tab, layar bersarang di tab, dll). Silakan merobek jawaban ini di komentar :)
Sebagai tanda bahwa solusi ini tidak terlalu baik adalah bahwa baru-baru ini saya harus menambahkan beberapa fungsi navigasi ke aplikasi. Beberapa tombol aneh yang harus membawa pengguna dari satu tab ke tab lain dan ke layar bersarang. Melakukan hal itu secara terprogram adalah rasa sakit di pantat, karena siapa-tahu-siapa masalah dan berurusan dengan kapan fragmen dan kegiatan sebenarnya dipakai dan diinisialisasi. Saya pikir itu akan jauh lebih mudah jika layar dan tab itu semua benar-benar hanya Tampilan.
Terakhir, jika Anda perlu selamat dari perubahan orientasi, penting bahwa fragmen Anda dibuat menggunakan setArguments / getArguments. Jika Anda menetapkan variabel contoh di konstruktor fragmen Anda, Anda akan kacau. Tapi untungnya itu sangat mudah diperbaiki: cukup simpan semuanya di setArguments di konstruktor dan kemudian ambil hal-hal itu dengan getArguments di onCreate untuk menggunakannya.