Я пытаюсь реализовать вкладки для навигации в приложении для Android. Так как TabActivity и ActivityGroup устарели, я хотел бы реализовать его, используя вместо этого фрагменты.
Я знаю, как настроить один фрагмент для каждой вкладки, а затем переключать фрагменты при нажатии на вкладку. Но как я могу иметь отдельный задний стек для каждой вкладки?
Например, фрагменты A и B будут находиться под вкладкой 1, а фрагменты C и D - под вкладкой 2. При запуске приложения отображается фрагмент A и выбирается вкладка 1. Затем фрагмент A можно заменить фрагментом B. Когда выбрана вкладка 2, должен отображаться фрагмент C. Если выбрана вкладка 1, фрагмент B должен снова отображаться. На этом этапе должна быть возможность использовать кнопку «Назад» для отображения фрагмента А.
Также важно, чтобы состояние каждой вкладки поддерживалось при повороте устройства.
BR Martin
источник
Я ужасно опоздал на этот вопрос. Но так как эта тема была очень информативной и полезной для меня, я подумал, что лучше разместить здесь свои два пенса.
Мне нужен был такой экран (минималистичный дизайн с 2 вкладками и 2 представлениями на каждой вкладке),
У меня были те же требования в прошлом, и я делал это, используя
TabActivityGroup
(что в то время тоже устарело) и Activity. На этот раз я хотел использовать фрагменты.Так вот как я это сделал.
1. Создайте базовый класс фрагментов.
Все фрагменты в вашем приложении могут расширять этот базовый класс. Если вы хотите использовать такие специальные фрагменты, как,
ListFragment
вы должны создать базовый класс для этого тоже. Вам будет понятно об использованииonBackPressed()
иonActivityResult()
если вы прочитаете пост в полном объеме ..2. Создайте несколько идентификаторов табуляции, доступных везде в проекте
тут нечего объяснять ..
3. Хорошо, главная вкладка «Активность» - просмотрите комментарии в коде.
4. app_main_tab_fragment_layout.xml (на случай, если кому-то интересно)
5. AppTabAFirstFragment.java (первый фрагмент на вкладке A, аналог для всех вкладок)
Это может быть не самым изысканным и правильным способом. Но это работало прекрасно в моем случае. Также у меня было только это требование в портретном режиме. Мне никогда не приходилось использовать этот код в проекте, поддерживающем обе ориентации. Так что не могу сказать, с какими проблемами я сталкиваюсь там ..
РЕДАКТИРОВАТЬ :
Если кому-то нужен полный проект, я отправил пример проекта на github .
источник
Нам пришлось реализовать то же поведение, которое вы недавно описали для приложения. Экраны и общий поток приложения уже были определены, поэтому нам пришлось придерживаться его (это клон приложения для iOS ...). К счастью, нам удалось избавиться от экранных кнопок назад :)
Мы взломали решение, используя смесь TabActivity, FragmentActivities (мы использовали библиотеку поддержки для фрагментов) и Fragments. В ретроспективе, я почти уверен, что это было не лучшее архитектурное решение, но нам удалось заставить его работать. Если бы мне пришлось делать это снова, я бы, вероятно, попытался бы сделать решение, основанное на более активных действиях (без фрагментов), или попытаться иметь только одну операцию для вкладок, и позволить всем остальным быть представлениями (которые я считаю гораздо более многоразовые, чем деятельность в целом).
Поэтому требовалось, чтобы на каждой вкладке было несколько вкладок и вложенных экранов:
и т.д...
Скажем так: пользователь начинает с вкладки 1, переходит с экрана 1 на экран 2, затем на экран 3, затем он переключается на вкладку 3 и переходит с экрана 4 на 6; если он переключился обратно на вкладку 1, он должен снова увидеть экран 3, а если он нажал кнопку Назад, он должен вернуться к экрану 2; Назад снова, и он на экране 1; перейдите на вкладку 3, и он снова на экране 6.
Основным действием в приложении является MainTabActivity, которое расширяет TabActivity. Каждая вкладка связана с действием, скажем ActivityInTab1, 2 и 3. И тогда каждый экран будет фрагментом:
Каждый ActivityInTab содержит только один фрагмент за раз и знает, как заменить один фрагмент на другой (почти так же, как ActvityGroup). Круто то, что таким способом довольно легко создавать отдельные задние стеки для каждой вкладки.
Функциональность каждого ActivityInTab была совершенно одинаковой: знать, как перемещаться от одного фрагмента к другому и поддерживать задний стек, поэтому мы поместили это в базовый класс. Давайте назовем это просто ActivityInTab:
Activity_in_tab.xml просто так:
Как видите, макет представления для каждой вкладки был одинаковым. Это потому, что это просто FrameLayout с именем content, который будет содержать каждый фрагмент. Фрагменты - это те, которые имеют вид каждого экрана.
Что касается бонусных баллов, мы также добавили небольшой код для отображения диалогового окна подтверждения, когда пользователь нажимает кнопку «Назад», и больше нет фрагментов для возврата:
Это в значительной степени установка. Как вы можете видеть, каждая FragmentActivity (или просто просто Activity в Android> 3) берет на себя все бэк-стекирование со своим собственным FragmentManager.
Такое действие, как ActivityInTab1, будет действительно простым, оно просто покажет свой первый фрагмент (то есть экран):
Затем, если фрагменту нужно перейти к другому фрагменту, он должен выполнить небольшое грязное приведение ... но это не так уж плохо:
Вот и все. Я почти уверен, что это не очень каноническое (и в основном не очень хорошее) решение, поэтому я хотел бы спросить опытных разработчиков Android, как лучше использовать эту функциональность, а если нет, то как готово »в Android, я был бы признателен, если бы вы указали мне ссылку или материал, который объясняет, как Android подходит к этому (вкладки, вложенные экраны в вкладках и т. д.). Не стесняйтесь разрывать этот ответ в комментариях :)
В знак того, что это решение не очень удачное, в последнее время мне пришлось добавить в приложение некоторые функции навигации. Некоторая причудливая кнопка, которая должна перенести пользователя из одной вкладки в другую и во вложенный экран. Выполнение этого программно было болезненным задом, из-за проблем «кто-знает-кто» и решения, когда фрагменты и действия фактически создаются и инициализируются. Я думаю, что было бы намного проще, если бы эти экраны и вкладки были просто представлениями.
Наконец, если вам нужно пережить изменения ориентации, важно, чтобы ваши фрагменты создавались с помощью setArguments / getArguments. Если вы установите переменные экземпляра в конструкторы ваших фрагментов, вы будете испорчены. Но, к счастью, это действительно легко исправить: просто сохраните все в setArguments в конструкторе и затем извлеките эти вещи с помощью getArguments в onCreate, чтобы использовать их.
источник
Это может быть легко достигнуто с ChildFragmentManager
Вот пост об этом со связанным проектом. взглянуть,
http://tausiq.wordpress.com/2014/06/06/android-multiple-fragments-stack-in-each-viewpager-tab/
источник
Хранение сильных ссылок на фрагменты не является правильным способом.
FragmentManager предоставляет
putFragment(Bundle, String, Fragment)
иsaveFragmentInstanceState(Fragment)
.Либо одного достаточно, чтобы реализовать backstack.
Используя
putFragment
вместо замены фрагмента, вы отделяете старый и добавляете новый. Это то, что платформа делает для замены транзакции, которая добавляется в backstack.putFragment
сохраняет индекс для текущего списка активных фрагментов, и эти фрагменты сохраняются платформой во время изменения ориентации.Второй способ, используя
saveFragmentInstanceState
, сохраняет все состояние фрагмента в Bundle, позволяя вам действительно удалить его, а не отсоединять. Использование этого подхода облегчает манипулирование задним стеком, поскольку вы можете извлекать фрагмент в любое время.Я использовал второй метод для этого варианта использования:
Я не хочу, чтобы пользователь возвращался к третьему экрану регистрации, нажав кнопку «Назад». Я также делаю анимацию между ними (используя
onCreateAnimation
), чтобы хакерские решения не работали, по крайней мере, если бы пользователь явно не заметил, что что-то не так.Это допустимый сценарий использования пользовательского backstack, делающий то, что ожидает пользователь ...
источник
Отказ от ответственности:
Я чувствую, что это лучшее место для публикации соответствующего решения, над которым я работал, для решения проблемы аналогичного типа, которая кажется довольно стандартной для Android. Это не решит проблему для всех, но может помочь некоторым.
Если основное различие между вашими фрагментами состоит только в данных, их поддерживающих (т. Е. Не в большом количестве различий в разметке), то вам, возможно, не потребуется фактически заменять фрагмент, а просто поменять местами базовые данные и обновить представление.
Вот описание одного возможного примера для этого подхода:
У меня есть приложение, которое использует ListViews. Каждый элемент в списке является родителем с некоторым количеством детей. При нажатии на элемент необходимо открыть новый список с этими дочерними элементами на той же вкладке ActionBar, что и исходный список. Эти вложенные списки имеют очень похожую компоновку (возможно, некоторые условные изменения здесь и там), но данные отличаются.
Это приложение имеет несколько уровней потомков под начальным родительским списком, и мы можем иметь или не иметь данные с сервера к тому времени, когда пользователь пытается получить доступ к какой-либо определенной глубине за пределами первого. Поскольку список состоит из курсора базы данных, а фрагменты используют загрузчик курсора и адаптер курсора, чтобы заполнить представление списка элементами списка, все, что должно произойти при регистрации клика:
1) Создайте новый адаптер с соответствующими полями «to» и «from», которые будут соответствовать новым представлениям элементов, добавляемых в список, и столбцам, возвращаемым новым курсором.
2) Установите этот адаптер в качестве нового адаптера для ListView.
3) Создайте новый URI на основе элемента, по которому щелкнули, и перезапустите загрузчик курсора с новым URI (и проекцией). В этом примере URI отображается на конкретные запросы с аргументами выбора, передаваемыми из UI.
4) Когда новые данные были загружены из URI, поменяйте курсор, связанный с адаптером, на новый курсор, и список обновится.
С этим не связано никакого обратного стека, поскольку мы не используем транзакции, поэтому вам придется либо создавать свои собственные, либо воспроизводить запросы в обратном порядке при выходе из иерархии. Когда я попытался это сделать, запросы были достаточно быстрыми, чтобы я просто выполнял их снова в oNBackPressed () до тех пор, пока не достиг вершины иерархии, после чего платформа снова переходит на кнопку возврата.
Если вы оказались в подобной ситуации, обязательно прочитайте документы: http://developer.android.com/guide/topics/ui/layout/listview.html
http://developer.android.com/reference/android/support/v4/app/LoaderManager.LoaderCallbacks.html
Я надеюсь, что это поможет кому-то!
источник
У меня была точно такая же проблема, и я реализовал проект github с открытым исходным кодом, который охватывает сложную навигацию по вкладкам, назад и вверх и хорошо протестирован и задокументирован:
https://github.com/SebastianBaltesObjectCode/PersistentFragmentTabs
источник
Это сложная проблема, поскольку Android обрабатывает только 1 задний стек, но это возможно. Мне понадобилось несколько дней, чтобы создать библиотеку с названием Tab Stacker, которая будет делать именно то, что вы ищете: историю фрагментов для каждой вкладки. Он имеет открытый исходный код и полностью документирован, и может быть легко включен с Gradle. Вы можете найти библиотеку на github: https://github.com/smart-fun/TabStacker
Вы также можете загрузить пример приложения, чтобы увидеть, что поведение соответствует вашим потребностям:
https://play.google.com/apps/testing/fr.arnaudguyon.tabstackerapp
Если у вас есть какие-либо вопросы, не стесняйтесь написать письмо.
источник
Я хотел бы предложить свое собственное решение на случай, если кто-то ищет и хочет попробовать выбрать лучшее для его / ее потребностей.
https://github.com/drusak/tabactivity
Цель создания библиотеки довольно банальна - реализовать ее как iPhone.
Основные преимущества:
источник
ListFragment
s в дополнение кFragment
s, поэтому я продублировал BaseTabFragment.java в BaseTabListFragment.java и расширил список ListFragment. Затем мне пришлось изменить различные части кода, где всегда предполагалось ожидать BaseTabFragment. Есть ли способ лучше?Простое решение:
Каждый раз, когда вы меняете вкладку / вызов просмотра root:
Это очистит BackStack. Не забудьте позвонить, прежде чем изменить корневой фрагмент.
И добавьте фрагменты с этим:
Обратите внимание, что
.addToBackStack(null)
и,transaction.add
например, можно изменить с помощьюtransaction.replace
.источник
Эта тема была очень очень интересной и полезной.
Спасибо Кришнабхадре за ваше объяснение и код, я использую ваш код и немного улучшил его, позволив сохранить стеки, currentTab и т. Д. От изменения конфигурации (в основном вращаясь).
Протестировано на реальных устройствах 4.0.4 и 2.3.6, не протестировано на эмуляторе
Я изменяю эту часть кода на «AppMainTabActivity.java», остальные остаются прежними. Может быть, Кришнабхадра добавит это в свой код.
Восстановить данные по созданию:
Сохраните переменные и поместите в Bundle:
Если существует предыдущий CurrentTab, установите это, иначе создайте новый Tab_A:
Я надеюсь, что это помогает другим людям.
источник
Я бы порекомендовал не использовать backstack на основе HashMap> в режиме «не держать действия» много ошибок. Он не будет правильно восстанавливать состояние, если вы глубоко в стеке фрагмента. А также будет разбит на фрагмент вложенной карты (за исключением: фрагмент не найден для идентификатора). Coz HashMap> после того, как приложение background \ foreground будет нулевым
Я оптимизирую код выше для работы с backstack фрагмента
Это нижняя TabView
Основной вид деятельности
tags_activity.xml
<
tags_icon.xml
источник