История в каждой вкладке или 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…
ссылка на оригинал статьи https://habrahabr.ru/post/317760/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *