Історія у кожній вкладці або multiple backstack



Проблема
З виходом android 3.0 (Api Level 11) в android з'явилися фрагменти, і так вже у розробників google вийшло, що для них підтримується лише один backstack. Не завжди дизайнери і замовники хочуть враховувати цю особливість. А іноді просто хочуть повну копію вже існуючого ios додатки.

Припустимо нам потрібно зробити подобу Tab Bar на android, в тому числі із збереженням історії в кожній вкладці. Але у нас один backstack, і що ж нам робити? Завдання здається неможливою.


Дослідження
«Якщо не запитати, ніколи не дізнаєшся. Якщо знаєш, потрібно лише запитати.»
З одного боку це суперечить офіційному guide (дивись Behavior), в якому однозначно написано, що навігація через низ повинна скидати стан.
Але кого це хвилює, коли мова йде про зручність користувача? Поклавши руку на серце, ви визнаєте, що так, як рекомендується — зручніше?

І як же все-таки зробити, що здавалося б неможливо через обмеження платформи? На розум приходять кілька варіантів один складніше іншого, а пошук в google видає і того більше жахливих костыльных рішень.

Але навіщо гадати, якщо можна підглянути як зроблено в Instagram? До слова сказати, Instagram так працював не завжди, були часи, коли дизайн Instagram був з вкладками, а історія при перемиканні скидалася.

Декомпіліруем apk Instagram за допомогою apktool і дивимося, що там. Головне activity програми — com.instagram.android.activity.MainTabActivity, дивимося від чого вона успадкована від класу com/instagram/base/activity/tabactivity/a, який в свою чергу успадкований від android/app/ActivityGroup. Далі можна не копати.

Такі класи, як ActivityGroup, TabActivity, LocalActivityManager — deprecated з 13 Api Level, тобто майже відразу, як з'явилися фрагменти. developer.android.com для цих класів написано наступне:
This class was deprecated in API level 13.
Use the new Fragment and FragmentManager APIs instead; these are also available on older platforms through the Android compatibility package.
Всі знають, що використовувати deprecated в новій разрабтке недобре. Всі кинулися писати на фрагментах і класи були віддані забуттю.

Рішення?

Мабуть, це єдине робоче рішення. Воно працює «з коробки», ніяких милиць (deprecated адже не вважається). Особисто я просто забув про LocalActivityManager, хоча почав розробляти під android ще в ті часи, коли телефонів з 8 Api Level було більше, ніж інших, але вони активно витіснялися.

Скрізь настільки наполегливо стверджують, що фрагменти наше все, а тенденція розробки single activity application така непорушна, що ті, хто долучився до розробки на android після 2011, швидше за все просто нічого не чули про LocalActivityManager.

Це просте рішення, нерозумно ним не скористатися. У кожній вкладці у нас буде своя activity зі своїм життєвим циклом, а головне своїм backstack'ом!

Трохи коду
Використовувати TabHost просто. Якщо знати, що шукати, можна знайти багато стародавніх туториалов, як ним користуватися. Інтернет пам'ятає.

Layout для нашого головного activity:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TabHost
android:id="@android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/bottom_bar"
android:layout_below="@+id/top">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TabWidget
android:id="@android:id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"></TabWidget>

<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="match_parent"></FrameLayout>
</LinearLayout>
</TabHost>
</FrameLayout>


Власне код:

//Можна було б унаследоваться від TabActivity (який теж deprecated),
//Але навіщо нам два deprecated класу, якщо можна обійтися одним?
public class MainActivity extends android.app.ActivityGroup {

TabHost mTabHost;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R. layout.activity_main);
mTabHost = (TabHost)findViewById(android.R.id.tabhost);
mTabHost.setup(getLocalActivityManager());

TabHost.TabSpec tabSpec;

tabSpec = mTabHost.newTabSpec("tag1");
tabSpec.setIndicator("Вкладка 1");//use getString

//TabActivity повинна бути в AndroidManifest
tabSpec.setContent(new Intent(this, TabActivity.class));
mTabHost.addTab(tabSpec);

tabSpec = mTabHost.newTabSpec("tag2");
tabSpec.setIndicator("Вкладка 2");//use getString

//TabActivity повинна бути в AndroidManifest
tabSpec.setContent(new Intent(this, TabActivity.class));
mTabHost.addTab(tabSpec);
}
}


P. s.
Дуже шкода, що доводиться використовувати deprecated класи, але поки google не зробить іншого рішення — це єдиний адекватний варіант. Можна побудувати зручну навігацію з одним backstack'ом, є інші розумні обмеження платформи, які необхідно враховувати та які обґрунтовані, але в даному випадку здається, що google просто упустив таку можливість при проектуванні Fragment Api.

Так вже виходить, що в цьому моменті android виявився вже точно не крутіше iphone…
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.