Нам довелося реалізувати саме таку поведінку, яку ви описували для програми нещодавно. Екрани та загальний потік програми були вже визначені, тому нам довелося дотримуватися цього (це клон додатків для iOS ...). На щастя, нам вдалося позбутися екранних кнопок повернення :)
Ми зламали рішення, використовуючи суміш TabActivity, FragmentActivities (ми використовували бібліотеку підтримки для фрагментів) та Fragments. В ретроспективі я майже впевнений, що це було не найкраще архітектурне рішення, але нам вдалося налагодити роботу. Якби мені довелося це зробити ще раз, я, мабуть, спробую зробити більш активне рішення (без фрагментів) або спробувати лише один Активність для вкладок, а всі інші - перегляди (які я вважаю набагато більше багаторазове використання, ніж загальна діяльність).
Тому вимоги полягали в тому, щоб у кожній вкладці були деякі вкладки та нестабільні екрани:
tab 1
screen 1 -> screen 2 -> screen 3
tab 2
screen 4
tab 3
screen 5 -> 6
тощо ...
Так скажіть: користувач починає вкладку 1, переходить з екрана 1 на екран 2, потім на екран 3, потім він переходить на вкладку 3 і переходить з екрана 4 на 6; якщо він перейшов на вкладку 1, він знову побачить екран 3, а якщо натиснути Назад, він повинен повернутися до екрану 2; Знову назад, і він знаходиться на екрані 1; перейдіть на вкладку 3 і він знову на екрані 6.
Основна діяльність у додатку - MainTabActivity, яка розширює TabActivity. Кожна вкладка пов'язана з діяльністю, скажімо ActivityInTab1, 2 і 3. І тоді кожен екран буде фрагментом:
MainTabActivity
ActivityInTab1
Fragment1 -> Fragment2 -> Fragment3
ActivityInTab2
Fragment4
ActivityInTab3
Fragment5 -> Fragment6
Кожен ActivityInTab містить лише один фрагмент за один раз і знає, як замінити один фрагмент на інший (майже такий самий, як ActvityGroup). Прикольна річ у тому, що таким чином досить легко добирати окремі спинки для кожної вкладки.
Функціонал для кожного ActivityInTab був абсолютно однаковий: знайте, як переходити від одного фрагмента до іншого та підтримувати задній стек, тому ми ставимо це в базовий клас. Назвемо це просто 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 - це саме таке:
<?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>
Як бачимо, схема перегляду для кожної вкладки була однаковою. Це тому, що це лише вміст FrameLayout, який називається вмістом, який вміщує кожен фрагмент. Фрагменти - це ті, що мають вигляд кожного екрана.
Для бонусних очок ми також додали невеликий код, щоб показати діалогове вікно підтвердження, коли користувач натискає Назад і більше немає фрагментів, на які можна повернутися:
// 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();
}
}
Це майже все налаштування. Як бачите, кожен FragmentActivity (або просто Активність в Android> 3) піклується про всі бек-стеки за допомогою власного FragmentManager.
Така діяльність, як ActivityInTab1, буде дуже простою, вона просто покаже, що це перший фрагмент (тобто екран):
public class ActivityInTab1 extends ActivityInTab {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
navigateTo(new Fragment1());
}
}
Потім, якщо фрагмент повинен перейти до іншого фрагмента, він повинен зробити невеликий неприємний кастинг ... але це не так вже й погано:
// In Fragment1.java for example...
// Need to navigate to Fragment2.
((ActivityIntab) getActivity()).navigateTo(new Fragment2());
Так що це майже все. Я впевнений, що це не дуже канонічне (і здебільшого впевнений, не дуже гарне) рішення, тому я хотів би запитати досвідчених розробників Android, що було б краще підходити до використання цієї функціональності, і якщо це не так "як це зроблено »в Android, я був би вдячний , якщо ви могли б вказати мені на яку - то посилання або матеріал , який пояснює , що Android спосіб наблизитися до цього (вкладки, вкладені екрани в закладках і т.д.). Сміливо відривайте цю відповідь у коментарях :)
Як знак того, що це рішення не дуже добре, це те, що останнім часом мені довелося додати деяку функціонал навігації у додаток. Якась химерна кнопка, яка має перенести користувача з однієї вкладки в іншу та вкладений екран. Це робити програмно було болем у попці, через те, хто-хто-хто знає проблеми та вирішувати, коли фрагменти та діяльність насправді створені та ініціалізовані. Я думаю, було б набагато простіше, якби ці екрани та вкладки були справді просто Переглядами.
Нарешті, якщо вам потрібно пережити зміни орієнтації, важливо, щоб ваші фрагменти були створені за допомогою setArguments / getArguments. Якщо ви встановите змінні екземпляра в конструкторах ваших фрагментів, ви будете накручені. Але, на щастя, це виправити дуже просто: просто збережіть усе у setArgument у конструкторі, а потім отримайте ті речі за допомогою getArguments in onCreate, щоб використовувати їх.