Android Conductor вместо Fragments


Содержание

Фрагменты

Введение во фрагменты

Организация приложения на основе нескольких activity не всегда может быть оптимальной. Мир ОС Android довольно сильно фрагментирован и состоит из многих устройств. И если для мобильных аппаратов с небольшими экранами взаимодействие между разными activity выглядит довольно неплохо, то на больших экранах — планшетах, телевизорах окна activity смотрелись бы не очень в силу большого размера экрана. Собственно поэтому и появилась концепция фрагментов.

Фрагмент существует в контексте activity и имеет свой жизненный цикл, вне activity обособлено он существовать не может. Каждая activity может иметь несколько фрагментов.

Для начала работы с фрагментами создадим новый проект с пустой MainActivity. И вначале создадим первый фрагмент.

Во-первых, фрагменты содержат те же элементы управления, что и activity. Поэтому создадим в папке res/layout новый файл fragment_content.xml :

Здесь определены кнопка и текстовое поле, которые будут составлять интерфейс фрагмента.

Теперь создадим сам класс фрагмента. Для этого добавим в одну папку с MainActivity новый класс. Для этого нажмем на папку правой кнопкой мыши и выберем в меню New -> Java Class . Назовем новый класс ContentFragment и определим у него следующее содержание:

Класс фрагмента должен наследоваться от класса Fragment .

Для создания визуального интерфейса фрагмент переопределяет родительский метод onCreateView() . Он принимает три параметра:

Объект LayoutInflater используется для установки ресурса разметки для создания интерфейса

Параметр ViewGroup container устанавливает контейнер интерфейса

Параметр Bundle savedInstanceState передает ранее сохраненное состояние

Для создания интерфейса применяется метод inflate() объекта LayoutInflater. Он получает ресурс разметки layout для данного фрагмента, контейнер, в который будет заключен интерфейс, и третий булевый параметр указывает, надо ли прикреплять разметку к контейнеру из второго параметра.

И как и в activity, здесь мы можем устанавливать обработчики дял элементов управления, обрабатывать их событий. В данном случае в текстовом поле выводится текущая дата.

Также стоит отметить, что в Andro >New -> Fragment -> Fragment(Blank) :

Данный шаблон сразу добавит в проект и класс фрагмента и связанный с ним класс разметки интерфейса.

И в конце надо добавить фрагмент в activity. Для этого изменим файл activity_main.xml , которая определяет интерфейс для MainActivity:

Каждый фрагмент задается с помощью элемента . Для каждого фрагмента должно быть установлены высота, ширина, id, а также имя. В качестве имени устанавливается полное имя класса с учетом пакета: andro

Кроме фрагмента мы можем добавить в разметку activity_main.xml мы можем добавить другие элементы или фрагменты, но в данном случае ограничимся одним фрагментом.

Код класса MainActivity остается тем же, что и при создании проекта:

Весь проект будет выглядеть следующим образом:

Если мы запустим приложение, то мы увидим фактически тот же самый интерфейс, который мы могли бы сделать и через activity, только в данном случае интерфейс будет определен во фрагменте:

Добавление фрагмента в коде

Кроме определения фрагмента в xaml-файле интерфейса мы можем добавить его динамически в activity.

Для этого изменим файл activity_main.xml :

И также изменим класс MainActivity :

Метод getSupportFragmentManager() возвращает объект FragmentManager , который управляет фрагментами. Но в данном случае надо сделать уточнение. В данном коде используется фрагмент — наследник класса android.support.v4.app.Fragment . Но для создания фрагментов мы также можем использовать класс android.app.Fragment из API 11. В случае использования android.app.Fragment менеджер фрагментов вызывается с помощью метода getFragmentManager() .

Объект FragmentManager с помощью метода beginTransaction() создает объект FragmentTransaction .

FragmentTransaction выполняет два метода: add() и commit(). Метод add() добавляет фрагмент: add(R.id.container, new ContentFragment()) — первым аргументом передается ресурс разметки, в который надо добавить фрагмент. И метод commit() подтвержает и завершает операцию добавления.

Итоговый результат такого добавления фрагмента будет тем же, что и при явном определении фрагмента через элемент в разметке интерфейса.

Почему фрагменты, а когда использовать фрагменты вместо действий?

В Android API 11+ Google выпустил новый класс под названием Fragment .

В видеороликах Google предлагает по возможности (link1, link2), мы должны использовать фрагменты вместо действий, но они не объяснили, почему именно.

Какова цель фрагментов и их возможное использование (за исключением некоторых примеров пользовательского интерфейса, которые могут быть легко достигнуты с помощью простых представлений/макетов)?

Мой вопрос о фрагментах:

  • Каковы цели использования фрагмента?
  • Каковы преимущества и недостатки использования фрагментов по сравнению с использованием действий/представлений/макетов?
  1. Можете ли вы дать некоторые интересные приложения для фрагментов? Что Google не упоминал в своих видеороликах?
  2. Каков наилучший способ общения между фрагментами и действиями, которые их содержат?
  3. Каковы наиболее важные вещи, которые следует помнить при использовании фрагментов? Любые советы и предупреждения из вашего опыта?


android android-layout android-activity android-3.0-honeycomb android-fragments

12 ответов

246 Решение Glenn Bech [2012-05-09 15:11:00]

# 1 и # 2, каковы цели использования фрагмента и каковы преимущества и недостатки использования фрагментов по сравнению с использованием мероприятия/виды/макеты?

Фрагменты — это решение для Android для создания пользовательских интерфейсов многократного использования. Вы можете достичь одних и тех же вещей, используя действия и макеты (например, с помощью include). Однако; фрагменты подключены к API Android, от HoneyComb и выше. Позвольте мне уточнить:

ActionBar . Если вам нужны закладки для навигации по вашему приложению, вы быстро увидите, что интерфейс ActionBar.TabListener дает вам FragmentTransaction в качестве входного аргумента методу onTabSelected . Возможно, вы могли бы проигнорировать это и сделать что-то еще и умное, но вы будете работать против API, а не с ним.

FragmentManager обрабатывает «назад» для вас очень умным способом. Назад не означает возврат к последнему действию, как для обычной деятельности. Это означает возврат к предыдущему состоянию фрагмента.

Вы можете использовать классный ViewPager с FragmentPagerAdapter для создания интерфейсов swipe. Код FragmentPagerAdapter намного чище, чем обычный адаптер, и он управляет экземплярами отдельных фрагментов.

Ваша жизнь будет намного проще, если вы будете использовать Фрагменты при попытке создать приложения для телефонов и планшетов. Поскольку фрагменты так привязаны к API Honeycomb +, вы захотите использовать их на телефонах, чтобы повторно использовать код. Это то, где библиотека совместимости пригодится.

Вы даже могли и должны использовать фрагменты для приложений, предназначенных только для телефонов. Если у вас есть мобильность в виду. Я использую ActionBarSherlock и библиотеки совместимости для создания приложений «ICS look», которые выглядят одинаково вплоть до версии 1.6. Вы получаете последние функции, такие как ActionBar , с вкладками, переполнением, панель действий split, viewpager и т.д.

Лучший способ общения между фрагментами — это намерения. Когда вы нажимаете что-то в фрагменте, вы обычно вызываете StartActivity() с данными на нем. Цель передается всем фрагментам запускаемой вами активности.

90 Jagan [2014-05-08 10:29:00]

Фрагмент является частью деятельности, которая вносит свой вклад в эту деятельность. Фрагмент можно рассматривать как суб-активность, тогда как полный экран, с которым пользователь взаимодействует, называется «активностью».

Активность может содержать несколько фрагментов. Активность может содержать 0 или несколько фрагментов на основе размера экрана.

Фрагмент может быть повторно использован в нескольких действиях, поэтому он действует как компонент многократного использования в действиях.

Фрагмент не может существовать независимо. Это должно быть всегда частью деятельности. Где, поскольку деятельность может существовать без какого-либо фрагмента.

Не уверен, какие видео вы имеете в виду, но я сомневаюсь, что они говорят, что вы должны использовать фрагменты вместо действий, потому что они не являются взаимозаменяемыми. Фактически существует довольно подробная запись в руководстве Dev, подумайте над ее чтением.

Короче говоря, фрагменты живут внутри действий, и каждая деятельность может содержать множество фрагментов. Подобно действиям, они имеют определенный жизненный цикл, в отличие от действий, они не являются компонентами приложения верхнего уровня. Преимущества фрагментов включают повторное использование кода и модульность (например, использование одного и того же вида списка во многих видах деятельности), включая возможность создания многопанельных интерфейсов (в основном полезных на планшетах). Основным недостатком является (дополнительная) сложность. Обычно вы можете добиться того же, что и с (пользовательскими) представлениями в нестандартном и менее надежном виде.

43 mani [2014-05-23 16:26:00]

Фрагмент — это часть пользовательского интерфейса приложения или поведения, который может быть помещен в Activity, который позволяет создавать более модульную структуру активности. Это не будет ошибкой, если мы скажем, что фрагмент является своего рода субактивностью.

Ниже приведены важные моменты об фрагменте:

Фрагмент имеет свой собственный макет и его собственное поведение с его собственными обратными вызовами жизненного цикла.

Вы можете добавлять или удалять фрагменты в действии во время работы.

Вы можете комбинировать несколько фрагментов в одном действии для создания пользовательского интерфейса с несколькими панелями.

Фрагмент может использоваться в нескольких действиях.

Жизненный цикл фрагмента тесно связан с жизненным циклом его активности хозяина.

Когда действие приостановлено, все фрагменты, доступные в работе, также будут остановлены.

Фрагмент может реализовать поведение, не имеющее компонент пользовательского интерфейса.

Фрагменты были добавлены в API Android в Android 3 (Honeycomb) с API версии 11.

Для получения более подробной информации посетите официальный сайт Fragments.

Это важная информация, которую я нашел на фрагментах:

Исторически каждый экран в приложении Android был реализован как отдельное действие. Это создает проблему при передаче информации между экранами, потому что механизм Намерения Android не позволяет передавать ссылочный тип (то есть объект) непосредственно между Операциями. Вместо этого объект должен быть сериализован или должна быть доступна глобально доступная ссылка.

Делая каждый экран отдельным фрагментом, эта головная боль при передаче данных полностью исключается. Фрагменты всегда существуют в контексте данного действия и всегда могут получить доступ к этому действию. Сохраняя интересующую информацию в Деятельности, Фрагмент для каждого экрана может просто получить доступ к ссылке на объект через Мероприятие.

Фрагменты особенно полезны в некоторых случаях, например, когда мы хотим сохранить навигационный ящик на всех наших страницах. Вы можете раздуть компоновку фрейма любым желаемым фрагментом и все еще иметь доступ к ящику навигации.

Если вы использовали какое-либо действие, вам пришлось бы держать ящик во всех действиях, которые делают для избыточного кода. Это одно интересное использование фрагмента.

Я новичок в Android и до сих пор считаю, что фрагмент полезен именно так.

Я знаю, что это уже обсуждалось до смерти, но я хотел бы добавить еще несколько замечаний:

Фраги могут использоваться для заполнения Menu и могут обрабатывать клики MenuItem самостоятельно. Таким образом, вы даете дополнительные варианты модуляции для своей деятельности. Вы можете создавать материалы ContextualActionBar и т.д., Не зная об этом, и можете в основном отделить его от основного материала, который обрабатывает ваша активность (Навигация/Настройки/О программе).

Родительский Frag с дочерними Frags может предоставить вам дополнительные возможности для модуляции ваших компонентов. Например. вы можете легко поменять Frags, поместить новые Frags внутри пейджера или удалить их, переставить их. Все без вашей деятельности, зная что-нибудь об этом, просто фокусируясь на материалах более высокого уровня.


1 Zealous [2020-03-19 10:33:00]

Фрагмент представляет собой поведение или часть пользовательского интерфейса в Activity. Вы можете объединить несколько фрагментов в одном действии для создания многоуровневого пользовательского интерфейса и повторного использования фрагмента в нескольких действиях. Вы можете представить фрагмент как модульный раздел активности, который имеет свой жизненный цикл, получает свои собственные события ввода и которые вы можете добавлять или удалять во время работы.

вы можете самостоятельно манипулировать каждым фрагментом, например добавлять или удалять их. Когда вы выполняете такую транзакцию фрагмента, вы также можете добавить ее в задний стек, который управляется активностью — каждая запись в предыдущем стеке в активности представляет собой запись о транзакции фрагмента, которая произошла. Задний стек позволяет пользователю отменить транзакцию фрагмента (перемещаться назад), нажав кнопку «Назад».

Когда вы добавляете фрагмент как часть вашего макета активности, он живет в ViewGroup внутри иерархии видов деятельности, а фрагмент определяет свой собственный макет представления. Вы можете вставить фрагмент в свой макет деятельности, объявив фрагмент в файле макета активности, как элемент или из вашего кода приложения, добавив его в существующую ViewGroup. Однако фрагмент не требуется, чтобы быть частью макета активности; вы также можете использовать фрагмент без собственного пользовательского интерфейса в качестве невидимого работника для этой деятельности.

Например: если вы использовали NavigationDrawer без фрагментов, лучше было бы сохранить экземпляр NavigationDrawer в одном действии и при навигации по нему, выбрав из элементов в NavigationDrawer, тогда каждый из этих запущенных действий не должен реализовывать NavigationDrawer, но вместо этого следует реализовать кнопку «Назад», чтобы перейти к «Главному»/единственному действию, которое было реализовано в NavigationDrawer.

Примечание. Если вы хотите реализовать NavigationDrawer в нескольких действиях, вам нужно будет воссоздать новый экземпляр NavigationDrawer в каждом действии, который вы хотите отобразить.

Я предполагаю, что это будет недостатком против использования фрагментов, тогда как если бы вы использовали фрагмент, вам не понадобилось бы много экземпляров ящика, вам понадобится только один.

Ящик с фрагментами вместо упражнений

Если вы используете NavigationDrawer с фрагментами, то ящик должен быть реализован в одном действии и, когда выбран каждый элемент ящика, их содержимое отображается в каждом из своих собственных фрагментов.

Связь между фрагментом и его активностью. Чтобы позволить фрагменту связываться с его активностью, вы можете определить интерфейс в классе Fragment и реализовать его в Activity. Фрагмент фиксирует реализацию интерфейса во время его жизненного цикла onAttach(), а затем может вызовите методы интерфейса, чтобы связаться с Activity.

Теперь фрагмент может доставлять сообщения в действие, вызывая метод onItemSelected() (или другие методы в интерфейсе), используя экземпляр mCallback интерфейса OnSelectedListener.

1. Предполагается использовать фрагмент?

  • Ans:
    1. Работа с различиями форм-фактора устройства.
    2. Передача информации между экранами приложений.
    3. Организация пользовательского интерфейса.
    4. Расширенные метафоры пользовательского интерфейса.

0 maniix [2020-05-15 15:46:00]

Фрагменты живут внутри Activity и имеют:

  • собственный жизненный цикл
  • собственный макет
  • его собственные дочерние фрагменты и т.д.

Подумайте о фрагментах как о субактивности основной деятельности, к которой она принадлежит, она не может существовать сама по себе и ее можно снова и снова называть/повторно использовать. Надеюсь, это поможет:)

Фрагмент живет внутри действия.

Пока действие живет само по себе.

Действия — это полноэкранные компоненты в приложении с панелью инструментов, все остальное предпочтительно фрагменты. Одно полноэкранное родительское действие с панелью инструментов может иметь несколько панелей, прокручиваемых страниц, диалогов и т.д. (Все фрагменты), все из которых могут быть доступны из родителя и взаимодействовать через родителя.

Деятельность A, Деятельность B, Деятельность C:

  • Все действия должны повторяться в одном и том же коде, например, для отображения базовой панели инструментов, или наследоваться от родительского действия (управление становится громоздким).
  • Чтобы перейти от одного действия к другому, либо все они должны находиться в памяти (накладные расходы), либо одно должно быть уничтожено, чтобы открылось другое.
  • Связь между действиями может быть сделана через Intents.

Занятие А, Фрагмент 1, Фрагмент 2, Фрагмент 3:

  • Нет повторения кода, все экраны имеют панели инструментов и т.д. Из этого одного действия.
  • Несколько способов перехода от одного фрагмента к следующему — просмотр пейджера, мультипанель и т.д.
  • Активность содержит большую часть данных, поэтому необходимо минимальное взаимодействие между фрагментами. Если все еще необходимо, может быть сделано через интерфейсы легко.
  • Фрагменты не должны быть полноэкранными, большая гибкость в их создании.
  • Фрагменты не нужно раздувать макет, если представления не нужны.
  • Несколько действий могут использовать один и тот же фрагмент.

Полный список

— динамически работаем с фрагментами

Размещать статические фрагменты мы уже умеем. Но, ясно дело, что гораздо интереснее работать с ними динамически. Система позволяет нам добавлять, удалять и заменять фрагменты друг другом. При этом мы можем сохранять все эти манипуляции в BackStack и кнопкой Назад отменять. В общем, все удобно и красиво.

Создадим простое приложение с двумя фрагментами, которое будет уметь:

— добавлять первый фрагмент
— удалять первый фрагмент
— заменять первый фрагмент вторым фрагментом
— переключать режим сохранения в BackStack операций с фрагментами

Project name: P1051_FragmentDynamic
Build Target: Android 4.1
Application name: FragmentDynamic
Package name: ru.startandroid.develop.p1051fragmentdynamic
Create Activity: MainActivity

В strings.xml добавим строки:

Создаем фрагменты. Как мы помним из прошлого урока, для этого нам нужны будут layout-файлы и классы, наследующие android.app.Fragment

Все почти аналогично прошлому уроку, только убрали вызовы кучи lifecycle методов с логами.

Цукерберг рекомендует:  Практические советы по найму и работе в команде

Рисуем основное Activity.

Три кнопки для добавления, удаления и замены фрагментов. Чекбокс для включения использования BackStack. И FrameLayout – это контейнер, в котором будет происходить вся работа с фрагментами. Он должен быть типа ViewGroup. А элементы Fragment, которые мы использовали на прошлом уроке для размещения фрагментов, нам не нужны для динамической работы.

В onCreate создаем пару фрагментов и находим чекбокс.


В onClick мы получаем менеджер фрагментов с помощью метода getFragmentManager. Этот объект является основным для работы с фрагментами. Далее, чтобы добавить/удалить/заменить фрагмент, нам необходимо использовать транзакции. Они аналогичны транзакциям в БД, где мы открываем транзакцию, производим операции с БД, выполняем commit. Здесь мы открываем транзакцию, производим операции с фрагментами (добавляем, удаляем, заменяем), выполняем commit.

Итак, мы получили FragmentManager и открыли транзакцию методом beginTransaction. Далее определяем, какая кнопка была нажата:

если Add, то вызываем метод add, в который передаем id контейнера (тот самый FrameLayout из main.xml) и объект фрагмента. В итоге, в контейнер будет помещен Fragment1

если Remove, то вызываем метод remove, в который передаем объект фрагмента, который хотим убрать. В итоге, фрагмент удалится с экрана.

если Replace, то вызываем метод replace, в который передаем id контейнера и объект фрагмента. В итоге, из контейнера удалится его текущий фрагмент (если он там есть) и добавится фрагмент, указанный нами.

Далее проверяем чекбокс. Если он включен, то добавляем транзакцию в BackStack. Для этого используем метод addToBackStack. На вход можно подать строку-тэг. Я передаю null.

Ну и вызываем commit, транзакция завершена.

Давайте смотреть, что получилось. Все сохраняем, запускаем приложение.

появился первый фрагмент.

Еще раз добавим первый фрагмент – жмем Add. И жмем Replace

первый фрагмент заменился вторым.

Жмем кнопку Назад. Приложение закрылось, т.к. все эти операции с фрагментами не сохранялись в BackStack. Давайте используем эту возможность.

Снова запускаем приложение и включаем чекбокс add to Back Stack

Выполняем те же операции: Add, Remove, Add, Replace. У нас добавится первый фрагмент, удалится первый фрагмент, добавится первый фрагмент, заменится вторым. В итоге мы снова видим второй фрагмент. Теперь жмем несколько раз кнопку Назад и наблюдаем, как выполняются операции, обратные тем, что мы делали. Когда транзакции, сохраненные в стеке закончатся, кнопка Назад закроет приложение.

Т.е. все достаточно просто и понятно. Скажу еще про пару интересных моментов.

Я в этом примере выполнял всего одну операцию в каждой транзакции. Но, разумеется, их может быть больше.

Когда мы удаляем фрагмент и не добавляем транзакцию в BackStack, то фрагмент уничтожается. Если же транзакция добавляется в BackStack, то, при удалении, фрагмент не уничтожается (onDestroy не вызывается), а останавливается (onStop).

В качестве самостоятельной работы: попробуйте немного изменить приложение и добавлять в один контейнер сразу два фрагмента. Возможно, результат вас удивит )

На следующем уроке:

— рассмотрим взаимодействие между Activity и ее фрагментами

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Красота из фрагментов. Как улучшить UI в Android c помощью класса Fragment

Содержание статьи

Грабли Activity

Большинство туториалов, демонстрирующих фишки Android-разработки, начинаются одинаково: неопытным разработчикам предлагают накидать все визуальные элементы прямо в XML-разметку главного Activity. Выглядит это примерно так:

Такая проектировка входит в привычку, и проект заполняется новыми Activity со все более сложной разметкой. Как итог — даже минимально полезное приложение обрастает стеком Activity, сжирающим всю оперативную память, а в разработчика летят камни и двойки в Google Play.

Сегодня я хочу поделиться с тобой широко известным «секретом»: Activity совсем не предназначены для массового использования. Наоборот, в приложении это штучный инструмент, который идет в ход только в крайних случаях. Повсеместная генерация новых Activity создает серьезные проблемы, которые делают работу приложения непредсказуемой. И даже если на твоем устройстве все стабильно, в мире немыслимое количество Android-устройств, на большинстве из которых твое приложение будет падать.

А все потому, что ОС Android совершенно не обещает держать твои Activity живыми. Как ты помнишь, эти компоненты существуют независимо друг от друга, обладая особым жизненным циклом. Если Activity переходит в состояние onPause , а происходит это довольно часто, он становится котейкой Шредингера: нельзя заранее знать, будет он жив или нет.

Используя дополнительные Activity для вывода меню и других мелочей, ты подвергаешь опасности все логические связи внутри приложения. Activity собираются в стек, и ОС может начать выгружать их по одному или группой. Когда пользователь захочет вернуться в предыдущее окно, Activity может уже быть уничтожен, и юзер выпадет из приложения.

Кроме проблем с сохранением логики, есть и рутина поддержки ООП-кода: плотно завязанные на Activity интерфейсы практически невозможно развивать дальше. Масштабирование, быструю замену одних элементов на другие, анимацию — все это будет очень тяжело реализовать в новых версиях приложения.

Благодаря моде на единую экосистему все мобильные приложения стали в целом очень похожи. Стоит только нащупать тропинку да немного потренироваться, и тогда качество начинает быстро расти. Следуя принципу «критикуя — предлагай», мы сейчас напишем приложение, реализующее универсальный пользовательский интерфейс. Это будет интересно не только с академической точки зрения — уверен, написанный сегодня код ты сможешь легко встроить в свои проекты. Итак, начнем!

Fragments

Чтобы работать с UI было проще и быстрее, Google создала фрагмент (Fragment) — класс — прослойку между Activity и визуальными составляющими программы. С одной стороны, это контейнер для любых View-объектов, которые могут быть показаны пользователю. С другой — продолжение Activity, от которого Fragment получает всю информацию об изменениях в жизненном цикле.

Рис. 1. Жизненный цикл фрагмента (с) Google

У фрагментов, как и у Activity, есть свой (правда, более оригинальный) жизненный цикл. К примеру, работать с UI сразу после создания фрагмента невозможно, нужно ждать загрузки всех элементов — после метода onCreate выполнится метод onCreateView , где и можно будет загрузить элементы.


В фрагменте тоже можно переопределять любые методы, отслеживающие состояние окна. Так, если приложение уйдет в фон, в Activity выполнится onPause , а затем метод с точно таким же названием выполнится здесь. Это может быть полезно — удобно для отключения от сторонних объектов, например привязанных сервисов (bound service).

FragmentTransaction

Зная, что работа с фрагментами будет насыщенной, Google заблаговременно создала для этого специальные инструменты. Классы FragmentManager и FragmentTransaction аккумулируют в себе все процессы: создание новых фрагментов и их удаление, передачу данных и так далее.

Объект FragmentManager создавать не нужно, он уже есть в каждом Activity, нужно только получить на него ссылку. А все действия будут проходить через FragmentTransaction , который затем самостоятельно передаст данные менеджеру фрагментов.

Хочу заметить, что классы, работающие с фрагментами, доступны в двух вариантах. Рекомендую использовать более новую версию — это те, у которых в пути импорта присутствует строчка android.support.v4. Это большая библиотека, созданная для организации обратной совместимости. Компания Google бережно относится к устройствам на всех версиях ОС, а библиотеки позволяют использовать новшества разработки даже при работе со старым API.

FrameLayout

Часто данные в UI приходят динамически, в зависимости от происходящих событий. Чтобы было куда поместить картинку или текст, существует специальный контейнер — FrameLayout . В этом и есть его основная задача — зарезервировать на экране место для любого объекта класса View, который можно будет подгрузить позднее. Фрагменты тоже живут в таких контейнерах.

Добавить новый фрагмент в FrameLayout можно по-разному: в FragmentTransaction доступны схожие по функциональности методы замены (replace) и добавления (add).

Несмотря на внешнюю схожесть, нужно хорошо понимать, какого результата ты можешь добиться. Метод replace работает очень просто — добавит фрагмент и, если в контейнере раньше что-то было, удалит старые фрагменты.

Метод add же создает стек из фрагментов, и каждый новый вызов помещает на верх стека новый экземпляр. Важно, что при появлении нового фрагмента старые объекты в стеке не получают никаких оповещений и работают так, как будто бы ничего не произошло. Это значит, что метод onPause для них не выполнится и они продолжат расходовать ресурсы устройства, хотя пользователь перестанет их видеть.

Такая растрата выделенной памяти бывает оправданна, например если хочется организовать предобработку данных: фрагмент может что-нибудь подгрузить, пока будет незаметен, а потом показать уже готовый результат.

Действий с фрагментами может быть несколько, они будут аккумулироваться до тех пор, пока не выполнится метод commit.

Рис. 2. Сменяемые фрагменты

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score! Подробнее

Why I choose Conductor over Fragment?

Mar 12 ’18 ・2 min read

If we go to repository page of Conductor it says that «A small, yet full-featured framework that allows building View-based Android applications». It really does what it says and there are too many pros than cons that I can talk about.

First thing first, it is very easy to integrate and helps you to build single activity application for Android. When Activities first appeared in the documentation of it is written that «a single, focused thing that the user can do». It means that you can do all the things inside one activity and handle all other things with the help of Conductor or Fragment. Moreover, with the single activity you can manage:

So what should turn you into choosing Conductor

Conductor has four main components that you should know first before starting.

As we all know when we are using Fragment we need FragmentManager to replace them. Conductor helps you to write minimal code for replacing Views. We should extend all our views to Controller and then if we want to change View calling getRouter().pushController(YourAnotherViewWhichIsController()) which is an important part of Conductor is enough.

The best part of Conductor is that other than Fragment its Controllers lifecycle is simpler to understand.

It’s navigation and backstack handling is best. Simpler like:

In conclusion, the thing main thing that turned me into Conductor is it’s easy to use structure, backstack handling, and easy lifecycle.

Innovating Andro > Kirill Akhmetov
  • Kirill Akhmetov
  • Andro >
  • The Conductor library daily grows in popularity. Unfortunately, however, there’s not a lot of guidance available on the web about using the library, and official sources give only examples. With that in mind, this blog is intended as an introductory course on using Conductor, helping you capitalize on its benefits while avoiding the most significant pitfalls. (This information will be useful for users who already have some experience in Android application development.)

    Conductor is marketed as a replacement for standard fragments. The primary idea is to wrap a View, thereby providing access to Activity lifecycle methods. Conductor has its own lifecycle, and it’s much easier than that of fragments. But it also has several quirks. (We’ll discuss those quirks a bit later.)

    Conductor’s main advantages include the following:

    • The code is simplified.
    • Transactions are executed immediately.
    • An entire application can be built using a single Activity.
    • You are not limited by the architecture of the app
    • Animation can be integrated easily.
    • There’s no need to save a state on configuration changes.

    Furthermore, you receive the following features straight out of the box:

    • You have the ability to work with the back stack.
    • Standard Activity callbacks can be accessed very easily.
    • Several standard animations are provided.
    • The lifecycle can be easily linked to RxJava.
    • It allows fast integration with ViewPager.

    Next, we are going to discuss several typical example situations which will be relevant in virtually any application. We are also going to explore the lifecycle of a Controller, which is the basic layout building block used by Conductor.


    Basics

    Let’s start with an easy example.

    After launching the app, a screen with the controller_home layout will appear.

    The Controller code is simple; it creates the View. The initialization process is more complex — it even contains a condition. During the process of Activity creation, Conductor::attachRouter attaches the router to our Activity and its lifecycle. It’s worth noting that we are sending not only the context and the container, but also the saved state. The router saves all the Controllers and their states in its stack. That way, if the Activity wasn’t created from the very beginning and was just restored, it’s not necessary to install the root Controller, because it will be restored from the saved state.

    Nothing prevents us from removing this check. However, every time we create an Activity, we are going to receive a new Controller and lose all saved data.

    States

    Let’s take a closer look at understanding how and where states are saved in Conductor. In order to do so, we’ll have to make our example a bit more complex. We’ll do this by creating an action that’s universally useful. We’re going to grow a tree using a tap.

    We haven’t seen anything supernatural yet. Note, however, that we are adding the link to the tree image in onDestroyView in order to make our View collectible by the garbage collector when the Controller is removed from the screen.

    What happens if we want to see the tree from the other side? Let’s change the orientation of the screen.

    Unfortunately, our tree disappeared. What can we do to make our application work?

    The lifecycle of Controllers is linked to a part of the code in the Activity. The fragment is created inside the Conductor::attachRouter function; thus, all requests to the lifecycle methods are linked with it and the Activity, which includes the initialization. The fragment has the setRetainInstance(true) parameter, so this fragment and all Controllers will survive any configuration changes. To make this happen, we simply need to save our state in a Controller variable.

    We added the isGrown variable, which keeps the state of the Controller and the method that updates the current state of the tree image. Once again, there’s nothing difficult here. Let’s check the result.

    When we change the configuration, the Activity is eliminated but the fragment is still alive. But what if our Activity has to be killed by the system, and not during the process of changing the configuration? Let’s see what happens when we select the “Don’t keep activities” parameter in the settings.

    No miracle happened; we lost all our data. In order to solve this problem, Conductor is duplicating the onSaveInstanceState and onRestoreInstanceState methods. Let’s implement them and confirm that everything works.

    Hurray! Now we can move on to more complex things, knowing that our tree is safe and sound.

    Sending Data

    Now let’s determine how many cones can grow on the tree. In order to do that, we will create a Controller that will receive the number of cones and show it.

    However, this code is not going to be compiled, because one constructor is still missing. Let’s explore the importance of that constructor.

    As with fragments, it’s not so easy. The Android system uses reflection to call a constructor without parameters. The Conductor library calls a constructor with a Bundle parameter.

    If we simply add a constructor and our Controller is eliminated, Conductor will create it once again, and we will lose all the information about the cones. In order to avoid this, we have to save our data in args. Args are saved by Conductor when the Controller is eliminated.

    A more elegant option using BundleBuilder can be seen in these examples.

    Next, we’ll add a call for the Controller and save the number of cones.

    The first tap grows the tree while the subsequent taps show the number of cones on it. Finally, in order to switch to another Controller, we have to request pushController from the router in order to add our Controller to the back stack and show it on the screen.

    Everything looks great now. Unfortunately, however, we can’t return without restarting the app. Let’s change this.

    This code will work really smoothly in the app. But let’s look more closely at the internal parts of onBackPressed. Let’s imagine that we don’t want to hide our application during the last stage. The first thing we would like to do is to keep only router.handleBack(). The thing is, when handleBack returns false, it doesn’t mean there was no work done. It means the last Controller was eliminated and the existence of the Activity (or any other container with a router) must be stopped. So you need to remember that even if you delete the root Controller, its View won’t be deleted from the stage. This is made to show a “qualitative” closing animation while the Activity is closing.

    That’s why we need to remember the following rule: when handleBack returns false, the owner of the router must be eliminated. Such behavior can be changed by calling the setPopsLastView method with a false parameter.

    Receiving Data

    Now it’s time to harvest our fruits. It is often required to send data to the previous Controller. Let’s try to collect several cones from the tree.

    We could simply send listener to the Controller, but the Android system upsets the apple cart. What if the Controller is going to be eliminated? We won’t be able to restore the link to the listener. The easiest way to solve this problem is to use the setTargetController method, which allows saving the link to another Controller and restoring it after re-creation of the Controller. Common courtesy is not to use the Controller type as a parameter; in fact, it’s preferable to use the interface type as the parameter. When using the interface type as a parameter, the Controller must implement the interface.

    We used the constructor to send the argument, which must inherit Controller, implement the interface of the listener, and save it using the setTargetController method.

    When we leave the Controller, we update the number of cones in HomeController by calling conesLeft(…) from the listener. And here’s the result.

    Animations

    Now we need to make things more attractive. With a subtle movement of the hand, we add animation to the transitions to show and hide the Controller. The basic animation set is available straight out of the box, but you can also use your own by overriding ControllerChangeHandler or inherited classes.

    Lifecycle Methods

    Conductor supports several other lifecycle methods in addition to those indicated in the official documents. They can be useful if you want to implement any unusual behavior. Let’s look at these methods:

    • onAttach — Called when the Controller is shown on the screen
    • onDetach — Called when the Controller is removed from the screen
    • onDestroyView — Called when the View attached to the Controller is eliminated
    • onCreateView — Called when the View must be created for the Controller
    • onDestroy — Called right before the Controller is eliminated

    All the methods described below are available in the lifecycle. However, they duplicate the following existing methods of View and Activity classes:

    • onSaveViewState
    • onRestoreViewState
    • onSaveInstanceState
    • onRestoreInstanceState


    The following methods are called in during the lifecycle. But it’s better not to rely on them. The order of their calls can depend on the animation, and the only thing which we can handle inside of them is an animation.

    • onChangeStarted — Called right before the animation
    • onChangeEnded — Called right after the animation

    When we tried to build a lifecycle, it turned out not to be as easy as it had been described on the official page. Controllers usually have two lifecycles. The first one is the cycle itself that works during the transition between Controllers. The second one works during the creation and the elimination of the Activity. They have several differences; that’s why the diagram became slightly bigger.

    Want to learn more about how Distillery is leveraging the latest technologies to streamline the product development process? Let us know!

    About the Author

    Since Kirill Akhmetov joined Distillery in 2020, he has already become an important part of the team. Back in the day, Kirill started as a C++ programmer, eventually moving on to ActionScript and Game Dev. Today, however, he’s fully devoted to Android development, constantly setting new goals for himself and achieving them in style. In his free time, Kirill enjoys hustle dancing.

    Фрагменты

    Фрагмент (класс Fragment ) представляет поведение или часть пользовательского интерфейса в операции (класс Activity ). Разработчик может объединить несколько фрагментов в одну операцию для построения многопанельного пользовательского интерфейса и повторного использования фрагмента в нескольких операциях. Фрагмент можно рассматривать как модульную часть операции. Такая часть имеет свой жизненный цикл и самостоятельно обрабатывает события ввода. Кроме того, ее можно добавить или удалить непосредственно во время выполнения операции. Это нечто вроде вложенной операции, которую можно многократно использовать в различных операциях.

    Фрагмент всегда должен быть встроен в операцию, и на его жизненный цикл напрямую влияет жизненный цикл операции. Например, когда операция приостановлена, в том же состоянии находятся и все фрагменты внутри нее, а когда операция уничтожается, уничтожаются и все фрагменты. Однако пока операция выполняется (это соответствует состоянию возобновлена жизненного цикла), можно манипулировать каждым фрагментом независимо, например добавлять или удалять их. Когда разработчик выполняет такие транзакции с фрагментами, он может также добавить их в стек переходов назад, которым управляет операция. Каждый элемент стека переходов назад в операции является записью выполненной транзакции с фрагментом. Стек переходов назад позволяет пользователю обратить транзакцию с фрагментом (выполнить навигацию в обратном направлении), нажимая кнопку Назад.

    Когда фрагмент добавлен как часть макета операции, он находится в объекте ViewGroup внутри иерархии представлений операции и определяет собственный макет представлений. Разработчик может вставить фрагмент в макет операции двумя способами. Для этого следует объявить фрагмент в файле макета операции как элемент или добавить его в существующий объект ViewGroup в коде приложения. Впрочем, фрагмент не обязан быть частью макета операции. Можно использовать фрагмент без интерфейса в качестве невидимого рабочего потока операции.

    В этом документе показано, как построить приложение, использующее фрагменты. В частности, обсуждается, как фрагменты могут поддерживать свое состояние, когда они добавляются в стек переходов назад операции, использовать события совместно с операцией и другими фрагментами внутри нее, выводить данные в строку действий операции и т. д.

    Философия проектирования

    Фрагменты впервые появились в Android версии 3.0 (API уровня 11), главным образом, для обеспечения большей динамичности и гибкости пользовательских интерфейсов на больших экранах, например, у планшетов. Поскольку экраны планшетов гораздо больше, чем у смартфонов, они предоставляют больше возможностей для объединения и перестановки компонентов пользовательского интерфейса. Фрагменты позволяют делать это, избавляя разработчика от необходимости управлять сложными изменениями в иерархии представлений. Разбивая макет операции на фрагменты, разработчик получает возможность модифицировать внешний вид операции в ходе выполнения и сохранять эти изменения в стеке переходов назад, которым управляет операция.

    Например, новостное приложение может использовать один фрагмент для показа списка статей слева, а другой—для отображения статьи справа. Оба фрагмента отображаются за одну операцию рядом друг с другом, и каждый имеет собственный набор методов обратного вызова жизненного цикла и управляет собственными событиями пользовательского ввода. Таким образом, вместо применения одной операции для выбора статьи, а другой — для чтения статей, пользователь может выбрать статью и читать ее в рамках одной операции, как на планшете, изображенном на рисунке 1.

    Следует разрабатывать каждый фрагмент как модульный и повторно используемый компонент операции. Поскольку каждый фрагмент определяет собственный макет и собственное поведение со своими обратными вызовами жизненного цикла, разработчик может включить один фрагмент в несколько операций. Поэтому он должен предусмотреть повторное использование фрагмента и не допускать, чтобы один фрагмент непосредственно манипулировал другим. Это особенно важно, потому что модульность фрагментов позволяет изменять их сочетания в соответствии с различными размерами экранов. Если приложение должно работать и на планшетах, и на смартфонах, можно повторно использовать фрагменты в различных конфигурациях макета, чтобы оптимизировать взаимодействие с пользователем в зависимости от доступного размера экрана. Например, на смартфоне может возникнуть необходимость в разделении фрагментов для предоставления однопанельного пользовательского интерфейса, если разработчику не удается поместить более одного фрагмента в одну операцию.

    Рисунок 1. Пример того, как два модуля пользовательского интерфейса, определенные фрагментами, могут быть объединены внутри одной операции для работы на планшетах, но разделены на смартфонах.

    Вернемся к примеру с новостным приложением. Оно может иметь два фрагмента, встроенных в Операцию А, когда выполняется на устройстве планшетного формата. В то же время на экране смартфона недостаточно места для обоих фрагментов, и поэтому Операция А включает в себя только фрагмент со списком статей. Когда пользователь выбирает статью, запускается Операция В, содержащая второй фрагмент для чтения статьи. Таким образом, приложение поддерживает как планшеты, так и смартфоны благодаря повторному использованию фрагментов в различных сочетаниях, как показано на рисунке 1.

    Подробные сведения относительно разработки приложения с различными сочетаниями фрагментов для различных конфигураций экрана приводятся в руководстве Поддержка планшетов и смартфонов

    Создание фрагмента

    Рисунок 2. Жизненный цикл фрагмента (во время выполнения операции)

    Для создания фрагмента необходимо создать подкласс класса Fragment (или его существующего подкласса). Класс Fragment имеет код, во многом схожий с кодом Activity . Он содержит методы обратного вызова, аналогичные методам операции, такие как onCreate() , onStart() , onPause() и onStop() . На практике, если требуется преобразовать существующее приложение Android так, чтобы в нем использовались фрагменты, достаточно просто переместить код из методов обратного вызова операции в соответствующие методы обратного вызова фрагмента.

    Как правило, необходимо реализовать следующие методы жизненного цикла:

    onCreate() Система вызывает этот метод, когда создает фрагмент. В своей реализации разработчик должен инициализировать ключевые компоненты фрагмента, которые требуется сохранить, когда фрагмент находится в состоянии паузы или возобновлен после остановки. onCreateView() Система вызывает этот метод при первом отображении пользовательского интерфейса фрагмента на дисплее. Для прорисовки пользовательского интерфейса фрагмента следует возвратить из этого метода объект View , который является корневым в макете фрагмента. Если фрагмент не имеет пользовательского интерфейса, можно возвратить null. onPause() Система вызывает этот метод как первое указание того, что пользователь покидает фрагмент (это не всегда означает уничтожение фрагмента). Обычно именно в этот момент необходимо фиксировать все изменения, которые должны быть сохранены за рамками текущего сеанса работы пользователя (поскольку пользователь может не вернуться назад).

    В большинстве приложений для каждого фрагмента должны быть реализованы, как минимум, эти три метода. Однако существуют и другие методы обратного вызова, которые следует использовать для управления различными этапами жизненного цикла фрагмента. Все методы обратного вызова жизненного цикла подробно обсуждаются в разделе Управление жизненным циклом фрагмента.

    Существует также ряд подклассов, которые, возможно, потребуется расширить вместо использования базового класса Fragment :

    DialogFragment Отображение перемещаемого диалогового окна. Использование этого класса для создания диалогового окна является хорошей альтернативой вспомогательным методам диалогового окна в классе Activity . Дело в том, что он дает возможность вставить диалоговое окно фрагмента в управляемый операцией стек переходов назад для фрагментов, что позволяет пользователю вернуться к закрытому фрагменту. ListFragment Отображение списка элементов, управляемых адаптером (например, SimpleCursorAdapter ), аналогично классу ListActivity . Этот класс предоставляет несколько методов для управления списком представлений, например, метод обратного вызова onListItemClick() для обработки нажатий. PreferenceFragment Отображение иерархии объектов Preference в виде списка, аналогично классу PreferenceActivity . Этот класс полезен, когда в приложении создается операция «Настройки».

    Добавление пользовательского интерфейса

    Фрагмент обычно используется как часть пользовательского интерфейса операции, при этом он добавляет в операцию свой макет.

    Чтобы создать макет для фрагмента, разработчик должен реализовать метод обратного вызова onCreateView() , который система Android вызывает, когда для фрагмента наступает время отобразить свой макет. Реализация этого метода должна возвращать объект View , который является корневым в макете фрагмента.

    Примечание. Если фрагмент является подклассом класса ListFragment , реализация по умолчанию возвращает класс ListView из метода onCreateView() , так что реализовывать его нет необходиомости.

    Чтобы возвратить макет из метода onCreateView() , можно выполнить его раздувание из ресурса макета, определенного в XML-файле. Для этой цели метод onCreateView() предоставляет объект LayoutInflater .

    Например, код подкласса класса Fragment , загружающий макет из файла example_fragment.xml , может выглядеть так:

    Создание макета

    В приведенном коде конструкция R.layout.example_fragment является ссылкой на ресурс макета по имени example_fragment.xml , хранящийся в ресурсах приложения. Подробные сведения о создании макета в XML см. в статье Пользовательский интерфейс.

    Параметр container , передаваемый методу onCreateView() , является родительским классом ViewGroup (из макета операции), в который будет вставлен макет фрагмента. Параметр savedInstanceState является классом Bundle , который предоставляет данные о предыдущем экземпляре фрагмента во время возобновления фрагмента (восстановление состояния подробно обсуждается в разделе Управление жизненным циклом фрагмента).

    Метод inflate() принимает три аргумента:

    • Идентификатор ресурса макета, раздувание которого следует выполнить.
    • Объект класса ViewGroup , который должен стать родительским для макета после раздувания. Передача параметра container необходима для того, чтобы система смогла применить параметры макета к корневому представлению раздутого макета, определяемому родительским представлением, в которое направляется макет.
    • Логическое значение, показывающее, следует ли прикрепить макет к объекту ViewGroup (второй параметр) во время раздувания. (В данном случае это false, потому что система уже вставляет раздутый макет в объект container , ипередача значения true создала бы лишнюю группу представления в окончательном макете).


    Мы увидели, как создавать фрагмент, предоставляющий макет. Теперь необходимо добавить фрагмент в операцию.

    Добавление фрагмента в операцию

    Как правило, фрагмент добавляет часть пользовательского интерфейса в операцию, и этот интерфейс встраивается в общую иерархию представлений операции. Разработчик может добавить фрагмент в макет операции двумя способами:

      объявив фрагмент в файле макета операции.

    В этом случае можно указать свойства макета для фрагмента, как будто он является представлением. Например, файл макета операции с двумя фрагментами может выглядеть следующим образом:

    Атрибут android:name в элементе определяет класс Fragment , экземпляр которого создается в макете.

    Когда система создает этот макет операции, она создает экземпляр каждого фрагмента, определенного в макете, и для каждого вызывает метод onCreateView() , чтобы получить макет каждого фрагмента. Система вставляет объект View , возвращенный фрагментом, непосредственно вместо элемента .

    Примечание. Каждый фрагмент должен иметь уникальный идентификатор, который система сможет использовать для восстановления фрагмента в случае перезапуска операции. (Что касается разработчика, он может использовать этот идентификатор для захвата фрагмента с целью выполнения транзакций с ним, например, чтобы удалить его). Предоставить идентификатор фрагменту можно тремя способами:

    • указать атрибут android:id с уникальным идентификатором;
    • указать атрибут android:tag с уникальной строкой;
    • ничего не предпринимать, чтобы система использовала идентификатор контейнерного представления.

    В любой момент выполнения операции разработчик может добавить фрагменты в ее макет. Для этого достаточно указать объект ViewGroup , в котором следует разместить фрагмент.

    Для выполнения транзакций с фрагментами внутри операции (таких как добавление, удаление или замена фрагмента) необходимо использовать API-интерфейсы из FragmentTransaction . Экземпляр класса FragmentTransaction можно получить от объекта Activity следующим образом:

    После этого можно добавить фрагмент методом add() , указав добавляемый фрагмент и представление, в которое он должен быть добавлен. Например:

    Первый аргумент, передаваемый методу add() , представляет собой контейнерный объект ViewGroup для фрагмента, указанный при помощи идентификатора ресурса. Второй параметр — это фрагмент, который нужно добавить.

    Выполнив изменения с помощью FragmentTransaction , необходимо вызвать метод commit() , чтобы они вступили в силу.

    Добавление фрагмента, не имеющего пользовательского интерфейса

    Пример, приведенный выше, демонстрирует, как добавлять в операцию фрагмент с предоставлением пользовательского интерфейса. Однако можно использовать фрагмент и для реализации фонового поведения операции без какого-либо дополнительного пользовательского интерфейса.

    Чтобы добавить фрагмент без пользовательского интерфейса, добавьте фрагмент из операции, используя метод add(Fragment, String) (передав ему уникальный строковый «тег» для фрагмента вместо идентификатора представления). Фрагмент будет добавлен, но, поскольку он не связан с представлением в макете операции, он не будет принимать вызов метода onCreateView() . Поэтому в реализации этого метода нет необходимости.

    Передача строкового тега свойственна не только фрагментам без пользовательского интерфейса, поэтому можно передавать строковые теги и фрагментам, имеющим пользовательский интерфейс. Однако, если у фрагмента нет пользовательского интерфейса, то строковый тег является единственным способом его идентификации. Если впоследствии потребуется получить фрагмент от операции, нужно будет вызвать метод findFragmentByTag() .

    Пример операции, использующей фрагмент в качестве фонового потока, без пользовательского интерфейса, приведен в образце кода FragmentRetainInstance.java , входящем в число образцов в SDK (и доступном при помощи Android SDK Manager). Путь к нему в системе — /APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java .

    Управление фрагментами

    Для управления фрагментами в операции нужен класс FragmentManager . Чтобы получить его, следует вызвать метод getFragmentManager() из кода операции.

    Ниже указаны действия, которые позволяет выполнить FragmentManager :

    • получать фрагменты, имеющиеся в операции, с помощью метода findFragmentById() (для фрагментов, предоставляющих пользовательский интерфейс в макете операции) или findFragmentByTag() (как для фрагментов, имеющих пользовательский интерфейс, так и для фрагментов без него);
    • снимать фрагменты со стека переходов назад методом popBackStack() (имитируя нажатие кнопки Назад пользователем);
    • регистрировать процесс-слушатель изменений в стеке переходов назад при помощи метода addOnBackStackChangedListener() .

    Дополнительные сведения об этих и других методах приводятся в документации по классу FragmentManager .

    Как было показано в предыдущем разделе, можно использовать класс FragmentManager для открытия FragmentTransaction , что позволяет выполнять транзакции с фрагментами, например, добавление и удаление.

    Выполнение транзакций с фрагментами

    Большим достоинством использования фрагментов в операции является возможность добавлять, удалять, заменять их и выполнять другие действия с ними в ответ на действия пользователя. Любой набор изменений, вносимых в операцию, называется транзакцией. Ее можно выполнить при помощи API-интерфейсов в FragmentTransaction . Каждую транзакцию можно сохранить в стеке переходов назад, которым управляет операция. Это позволит пользователю перемещаться назад по изменениям во фрагментах (аналогично перемещению назад по операциям).

    Экземпляр класса FragmentTransaction можно получить от FragmentManager , например, так:

    Каждая транзакция является набором изменений, выполняемых одновременно. Разработчик может указать все изменения, которые ему нужно выполнить в данной транзакции, вызывая методы add() , remove() и replace() . Затем, чтобы применить транзакцию к операции, следует вызвать метод commit() .

    Впрочем, до вызова метода commit() у разработчика может возникнуть необходимость вызвать метод addToBackStack() , чтобы добавить транзакцию в стек переходов назад по транзакциям фрагмента. Этим стеком переходов назад управляет операция, что позволяет пользователю вернуться к предыдущему состоянию фрагмента, нажав кнопку Назад.

    Например, следующий код демонстрирует, как можно заменить один фрагмент другим, сохранив при этом предыдущее состояние в стеке переходов назад:

    В этом коде объект newFragment замещает фрагмент (если таковой имеется), находящийся в контейнере макета, на который указывает идентификатор R.id.fragment_container . В результате вызова метода addToBackStack() транзакция замены сохраняется в стеке переходов назад, чтобы пользователь мог обратить транзакцию и вернуть предыдущий фрагмент, нажав кнопку Назад.

    Если в транзакцию добавить несколько изменений (например, еще раз вызвать add() или remove() ), а затем вызвать addToBackStack() , все изменения, примененные до вызова метода commit() , будут добавлены в стек переходов назад как одна транзакция, и кнопкаНазад обратит их все вместе.

    Порядок добавления изменений к объекту FragmentTransaction не играет роли за следующими исключениями:

    • метод commit() должен быть вызван в последнюю очередь;
    • если в один контейнер добавляется несколько фрагментов, то порядок их добавления определяет порядок, в котором они появляются в иерархии видов.

    Если при выполнении транзакции, удаляющей фрагмент, не вызвать метод addToBackStack() , при фиксации транзакции фрагмент уничтожается, и пользователь теряет возможность вернуться к нему. В то же время, если вызвать addToBackStack() при удалении фрагмента, фрагмент перейдет в состояние остановки и будет возобновлен, если пользователь вернется к нему.


    Совет. К каждой транзакции с фрагментом можно применить анимацию перехода, вызвав setTransition() до фиксации.

    Вызов метода commit() не приводит к немедленному выполнению транзакции. Метод запланирует ее выполнение в потоке пользовательского интерфейса операции (в «главном» потоке), как только у потока появится возможность для этого. Впрочем, при необходимости можно вызвать executePendingTransactions() из потока пользовательского интерфейса, чтобы транзакции, запланированные методом commit() были выполнены немедленно. Как правило, в этом нет необходимости, за исключением случаев, когда транзакция является зависимостью для заданий в других потоках.

    Внимание! Фиксировать транзакцию методом commit() можно только до того, как операциясохранит свое состояние (после того, как пользователь покинет ее). Попытка зафиксировать транзакцию после этого момента вызовет исключение. Дело в том, что состояние после фиксации может быть потеряно, если понадобится восстановить операцию. В ситуациях, в которых потеря фиксации не критична, следует вызывать commitAllowingStateLoss() .

    Взаимодействие с операцией

    Хотя Fragment реализован как объект, независимый от класса Activity , и может быть использован внутри нескольких операций, конкретный экземпляр фрагмента напрямую связан с содержащей его операцией.

    В частности, фрагмент может обратиться к экземпляру Activity с помощью метода getActivity() и без труда выполнить такие задачи, как поиск представления в макете операции:

    Аналогичным образом операция может вызывать методы фрагмента, получив ссылку на объект Fragment от FragmentManager с помощью метода findFragmentById() или findFragmentByTag() . Например:

    Создание обратного вызова события для операции

    В некоторых случаях необходимо, чтобы фрагмент использовал события совместно с операцией. Хороший способ реализации этого состоит в том, чтобы определить интерфейс обратного вызова внутри фрагмента и потребовать от контейнерной операции его реализации. Когда операция примет обратный вызов через этот интерфейс, она сможет обмениваться информацией с другими фрагментами в макете по мере необходимости.

    Пусть, например, у новостного приложения имеются два фрагмента в одной операции: один для отображения списка статей (фрагмент A), а другой—для отображения статьи (фрагмент B). Тогда фрагмент A должен сообщать операции о том, что выбран пункт списка, чтобы она могла сообщить фрагменту B о необходимости отобразить статью. В этом случае интерфейс OnArticleSelectedListener объявляется во фрагменте A:

    Тогда операция, содержащая этот фрагмент, реализует интерфейс OnArticleSelectedListener и переопределит метод onArticleSelected() , чтобы извещать фрагмент B о событии, исходящем от фрагмента A. Чтобы контейнерная операция наверняка реализовала этот интерфейс, метод обратного вызова onAttach() во фрагменте A (который система вызывает при добавлении фрагмента в операцию) создает экземпляр класса OnArticleSelectedListener , выполнив приведение типа объекта Activity , который передается методу onAttach() :

    Если операция не реализовала интерфейс, фрагмент генерирует исключение ClassCastException . В случае успеха элемент mListener будет содержать ссылку на реализацию интерфейса OnArticleSelectedListener в операции, чтобы фрагмент A мог использовать события совместно с операцией, вызывая методы, определенные интерфейсом OnArticleSelectedListener . Например, если фрагмент A является расширением класса ListFragment , то всякий раз, когда пользователь нажимает элемент списка, система вызывает onListItemClick() во фрагменте. Этот метод, в свою очередь, вызывает метод onArticleSelected() , чтобы использовать событие совместно с операцией:

    Параметр id , передаваемый методу onListItemClick() , — это идентификатор строки с выбранным элементом списка, который операция (или другой фрагмент) использует для получения статьи от объекта ContentProvider приложения.

    Дополнительные сведения о работе с поставщиком контента приводятся в документе Поставщики контента.

    Добавление элементов в строку действий

    Фрагменты могут добавлять пункты меню в Меню вариантов операции (и, следовательно, в Строку действий), реализовав onCreateOptionsMenu() . Однако, чтобы этот метод мог принимать вызовы, необходимо вызывать setHasOptionsMenu() во время выполнения метода onCreate() , чтобы сообщить, что фрагмент намеревается добавить пункты в Меню вариантов (в противном случае фрагмент не примет вызов метода onCreateOptionsMenu() ).

    Любые пункты, добавляемые фрагментом в Меню вариантов, присоединяются к уже существующим. Кроме того, фрагмент принимает обратные вызовы метода onOptionsItemSelected() , когда пользователь выбирает пункт меню.

    Разработчик может также зарегистрировать представление в макете своего фрагмента, чтобы предоставить контекстное меню. Для этого следует вызвать метод registerForContextMenu() . Когда пользователь открывает контекстное меню, фрагмент принимает вызов метода onCreateContextMenu() . Когда пользователь выбирает пункт меню, фрагмент принимает вызов метода onContextItemSelected() .

    Примечание. Хотя фрагмент принимает обратный вызов по событию «выбран пункт меню» для каждого добавленного им пункта, операция первой принимает соответствующий обратный вызов, когда пользователь выбирает пункт меню. Если имеющаяся в операции реализация обратного вызова по событию «выбран пункт меню» не обрабатывает выбранный пункт, событие передается методу обратного вызова во фрагменте. Это справедливо для Меню вариантов и контекстных меню.

    Подробные сведения относительно меню см. в руководствах для разработчиков Меню и Строка действий

    Управление жизненным циклом фрагмента

    Рисунок 3. Влияние жизненного цикла операции на жизненный цикл фрагмента

    Управление жизненным циклом фрагмента во многом аналогично управлению жизненным циклом операции. Как и операция, фрагмент может существовать в одном из трех состояний:

    Возобновлен Фрагмент виден во время выполнения операции. Приостановлен На переднем плане выполняется и находится в фокусе другая операция, но операция, содержащая данный фрагмент, по-прежнему видна (операция переднего плана частично прозрачна или не занимает весь экран). Остановлен Фрагмент не виден. Либо контейнерная операция остановлена, либо фрагмент удален из нее, но добавлен в стек переходов назад. Остановленный фрагмент по-прежнему активен (вся информация о состоянии и элементах сохранена в системе). Однако он больше не виден пользователю и будет уничтожен в случае уничтожения операции.

    Здесь снова просматривается аналогия с операцией: разработчик может сохранить состояние фрагмента с помощью Bundle на случай, если процесс операции будет уничтожен, а разработчику понадобится восстановить состояние фрагмента при повторном создании операции. Состояние можно сохранить во время выполнения метода обратного вызова onSaveInstanceState() во фрагменте и восстановить его во время выполнения onCreate() , onCreateView() или onActivityCreated() . Дополнительные сведения о сохранении состояния приводятся в документе Операции.

    Самое значительное различие в ходе жизненного цикла между операцией и фрагментом состоит в принципах их сохранения в соответствующих стеках переходов назад. По умолчанию операция помещается в управляемый системой стек переходов назад для операций, когда она останавливается (чтобы пользователь мог вернуться к ней с помощью кнопки Назад, как описано в статье Задачи и стек переходов назад). В то же время, фрагмент помещается в стек переходов назад, управляемый операцией, только когда разработчик явно запросит сохранение конкретного экземпляра, вызвав метод addToBackStack() во время транзакции, удаляющей фрагмент.

    В остальном управление жизненным циклом фрагмента очень похоже на управление жизненным циклом операции. Поэтому практические рекомендации по управлению жизненным циклом операций применимы и к фрагментам. При этом разработчику необходимо понимать, как жизненный цикл операции влияет на жизненный цикл фрагмента.

    Внимание! Если возникнет необходимость в объекте Context внутри объекта класса Fragment , можно вызвать метод getActivity() . Однако разработчик должен быть внимательным и вызывать метод getActivity() только когда фрагмент прикреплен к операции. Если фрагмент еще не прикреплен или был откреплен в конце его жизненного цикла, метод getActivity() возвратит null.

    Согласование с жизненным циклом операции

    Жизненый цикл операции, содержащей фрагмент, непосредственным образом влияет на жизненый цикл фрагмента, так что каждый обратный вызов жизненного цикла операции приводит к аналогичному обратного вызову для каждого фрагмента. Например, когда операция принимает вызов onPause() , каждый ее фрагмент принимает onPause() .

    Однако у фрагментов есть несколько дополнительных методов обратного вызова жизненого цикла, которые обеспечивают уникальное взаимодействие с операцией для выполнения таких действий, как создание и уничтожение пользовательского интерфейса фрагмента. Вот эти методы:

    onAttach() Вызывается, когда фрагмент связывается с операцией (ему передается объект Activity ). onCreateView() Вызывается для создания иерархии представлений, связанной с фрагментом. onActivityCreated() Вызывается, когда метод onCreate() , принадлежащий операции, возвращает управление. onDestroyView() Вызывается при удалении иерархии представлений, связанной с фрагментом. onDetach() Вызывается при разрыве связи фрагмента с операцией.

    Зависимость жизненого цикла фрагмента от содержащей его операции иллюстрируется рисунком 3. На этом рисунке можно видеть, что очередное состояние операции определяет, какие методы обратного вызова может принимать фрагмент. Например, когда операция принимает свой метод обратного вызова onCreate() , фрагмент внутри этой операции принимает всего лишь метод обратного вызова onActivityCreated() .

    Когда операция переходит в состояние «возобновлена», можно свободно добавлять в нее фрагменты и удалять их. Таким образом, жизненный цикл фрагмента может быть независимо изменен, только пока операция остается в состоянии «возобновлена».

    Однако, когда операция выходит из этого состояния, продвижение фрагмента по его жизненному циклу снова осуществляется операцией.

    Пример:

    Чтобы суммировать все сказанное в этом документе, рассмотрим пример операции, использующей два фрагмента для создания макета с двумя панелями. Операция, код которой приведен ниже, включает в себя один фрагмент для отображения списка пьес Шекспира, а другой — для отображения краткого содержания пьесы, выбранной из списка. В примере показано, как следует организовывать различные конфигурации фрагментов в зависимости от конфигурации экрана.

    Примечание. Полный исходный код этой операции находится в разделе FragmentLayout.java .

    Главная операция применяет макет обычным способом, в методе onCreate() :


    Здесь применяется макет fragment_layout.xml :

    Пользуясь этим макетом, система создает экземпляр класса TitlesFragment (список пьес), как только операция загрузит макет. При этом объект FrameLayout (в котором будет находиться фрагмент с кратким содержанием) занимает место в правой части экрана, но поначалу остается пустым. Как будет показано ниже, фрагмент не помещается в FrameLayout , пока пользователь не выберет элемент в списке.

    Однако не все экраны достаточно широки, чтобы отображать краткое содержание рядом со списком пьес. Поэтому описанный выше макет используется только при альбомной ориентации экрана и хранится в файле res/layout-land/fragment_layout.xml .

    Когда же устройство находится в книжной ориентации, система применяет макет, приведенный ниже, который хранится в файле res/layout/fragment_layout.xml :

    В этом макете присутствует только объект TitlesFragment . Это означает, что при книжной ориентации устройства виден только список пьес. Когда пользователь нажимает на элемент списка в этой конфигурации, приложение запускает новую операцию для отображения краткого содержания, а не загружает второй фрагмент.

    Далее можно видеть, как это реализовано в классах фрагмента. Вначале идет код класса TitlesFragment , отображающий список пьес Шекспира. Этот фрагмент является расширением класса ListFragment и использует его функции для выполнения основной работы со списком.

    Изучая код, обратите внимание на то, что в качестве реакции на нажатие пользователем элемента списка возможны две модели поведения. В зависимости от того, какой из двух макетов активен, либо в рамках одной операции создается и отображается новый фрагмент с кратким содержанием (за счет добавления фрагмента в объект FrameLayout ), либо запускается новая операция (отображающая фрагмент).

    Второй фрагмент, DetailsFragment , отображает краткое содержание пьесы, выбранной в списке TitlesFragment :

    Вспомним код класса TitlesFragment : если пользователь нажимает на пункт списка, а текущий макет не включает в себя представление R.id.details (которому принадлежит фрагмент DetailsFragment ), то приложение запускает операцию DetailsActivity для отображения содержимого элемента.

    Далее идет код класса DetailsActivity , который всего лишь содержит объект DetailsFragment для отображения краткого содержания выбранной пьесы на экране в книжной ориентации:

    Обратите внимание, что в альбомной конфигурации эта операция самостоятельно завершается, чтобы главная операция могла принять управление и отобразить фрагмент DetailsFragment рядом с фрагментом TitlesFragment . Это может произойти, если пользователь запустит операцию DetailsActivity в книжной ориентации экрана, а затем перевернет устройство в альбомную ориентацию (в результате чего текущая операция будет перезапущена).

    Дополнительные образцы кода, использующего фрагменты (и файлы с полным исходным кодом этого примера), доступны в приложении-примере API Demos в разделе ApiDemos (которое можно загрузить из компонента Samples SDK).

    Content and code samples on this page are subject to the licenses described in the Content License. Java is a registered trademark of Oracle and/or its affiliates.

    Управление Fragments Правильно

    У меня возникли некоторые трудности, правильно использование и управление фрагментами в моем проекте.

    Mainactivity расширяет SampleActivityBase и начинает простой фрагмент:

    Класс SlidingTabsbasicFragment расширяет фрагмент и возвращает макет onCreateView:

    Тогда в другом классе, который простирается PagerAdapter, содержание представления на основании положения изменяется:

    Это дает мне основные функциональные вкладки , что я ищу. Я даже изменить макет надувания нового на основе положения в порядке. Но теперь я пытаюсь реализовать пользовательский календарь под названием Caldroid .

    Основной класс здесь проходит FragmentActivity. Это позволяет setContentView и использовать FragmentTransaction и .Привернуть изменить компоновку с идентификатором в виде фрагмент с пользовательским календарем на нем:

    То, что я пытаюсь достичь является сохранение моих вкладок расположения и создание пользовательского календаря внутри только одной из вкладок. Я бы признателен за любые направления / помощь / ресурсы или альтернативные предложения.

    Я включил некоторые фрагменты кода, но оба проекта существует (более или менее) в качестве оригиналов:

    Спасибо за любую помощь, кто может обеспечить.

    * (1) Добавлено PagerAdapter по запросу: (? Из того, что я видел, как люди говорят, чтобы не возиться с instantiateItem)

    Вопрос про activity и fragment в andro >

    Используйте фрагменты там где это возможно.
    Например для групировки функционала (fragment1 — список статей на ресурсе, и fragment2 — сами статьи) у них будет общий активити. Дальше при реализации планшетного режима отображения можно очень просто организовать UI.

    Это нарушение принципов SOLID.
    У вас в одном классе будет очень много реализации + вы сами запутаетесь (замучаетесь) с BackStack.

    П.с. или свой активити писать как в Telegram.

    Даже в случае использования ND в главном окне, в приложении найдется много экранов, логике которых не обязательно иметь боковое меню.

    Все конечно зависит от проекта, но если например пользователь зашел в личный кабинет, меняет себе аватар профайла, зачем ему вдруг пункт меню «О приложении» или «Настройки»?

    Глубина стеков Activity — правильный путь. Исходить лучше из этого.

    Взять например PlayMarket. Если нажать на подробное описание релиза приложения, у вас нет возможности открыть боковое меню, хотя Activity не поменялся. Старайтесь обосновывать тот или иной функционал.

    Fragment вместо Activity

    Javascript
    12.05.2013, 02:59

    Activity и Fragment
    Всем добрый вечер. Создал приложение новостей с 2 Activity и 1 Adapter. Сейчас решил в проект.

    Fragment не передаётся в Activity
    Приложение для планшета. Учусь, как вложить Fragment в Activity Activity Class «MainActivity».

    Callbeck from Activity to Fragment
    При нажатии кнопки в активити должен отрабатывал метод в фрагменте (допустим обновляться вюшка). .

    Переход из Fragment в Activity
    как реализовать переход из Fragment В activity в фрагменте кнопка при нажатии которой происходит.

    Fragment не отображается на Activity
    Здравствуйте! Помогите, пожалуйста, разобраться. Суть такая: пользователь проходит упражнение по.

    Понравилась статья? Поделиться с друзьями:
    Все языки программирования для начинающих