Fragmentmanager для android что это

Фрагменты

Существует два основных подхода в использовании фрагментов.

Первый способ основан на замещении родительского контейнера. Создаётся стандартная разметка и в том месте, где будут использоваться фрагменты, размещается контейнер, например, FrameLayout. В коде контейнер замещается фрагментом. При использовании подобного сценария в разметке не используется тег fragment, так как его нельзя менять динамически. Также вам придётся обновлять ActionBar, если он зависит от фрагмента. Здесь показан такой пример.

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

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

Основные классы

Сами фрагменты наследуются от androidx.fragment.app.Fragment. Существует подклассы фрагментов: ListFragment, DialogFragment, PreferenceFragment, WebViewFragment и др. Не исключено, что число классов будет увеличиваться, например, появился ещё один класс MapFragment.

Для взаимодействия между фрагментами используется класс android.app.FragmentManager — специальный менеджер по фрагментам.

Как в любом офисе, спецманагер не делает работу своими руками, а использует помощников. Например, для транзакций (добавление, удаление, замена) используется класс-помощник android.app.FragmentTransaction.

Для сравнения приведу названия классов из библиотеки совместимости:

  • android.support.v4.app.FragmentActivity
  • android.support.v4.app.Fragment
  • android.support.v4.app.FragmentManager
  • android.support.v4.app.FragmentTransaction

Как видите, разница в одном классе, который я привёл первым. Он используется вместо стандартного Activity, чтобы система поняла, что придётся работать с фрагментами. На данный момент студия создаёт проект на основе ActionBarActivity, который является подклассом FragmentActivity.

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

В 2018 году Гугл объявила фрагменты из пакета androd.app устаревшими. Заменяйте везде на версию из библиотеки совместимости. В 2020 году уже используют пакет androidx.fragment.app.

В версии Support Library 27.1.0 появились новые методы requireActivity() и requireContext(), которые пригодятся при написании кода, когда требуется наличие активности и нужно избежать ошибки на null.

Общий алгоритм работы с фрагментами будет следующим:

У каждого фрагмента должен быть свой класс. Класс наследуется от класса Fragment или схожих классов, о которых говорилось выше. Это похоже на создание новой активности или нового компонента.

Также, как в активности, вы создаёте различные методы типа onCreate() и т.д. Если фрагмент имеет разметку, то используется метод onCreateView() — считайте его аналогом метода setContentView(), в котором вы подключали разметку активности. При этом метод onCreateView() возвращает объект View, который является корневым элементом разметки фрагмента.

Разметку для фрагмента можно создать программно или декларативно через XML.

Создание разметки для фрагмента ничем не отличается от создания разметки для активности. Вот отрывок кода из метода onCreateView():

Глядя на этот код, вы должные понять, что фрагмент использует разметку из файла res/layout/first_fragment.xml, которая содержит кнопку с идентификатором android:id=»@+id/button_first». Здесь также прослеживается сходство с подключением компонентов в активности. Обратите внимание, что перед методом findViewById() используется view, так как этот метод относится к компоненту, а не к активности, как мы обычно делали в программах, когда просто опускали имя активности. Т.е. в нашем случае мы ищем ссылку на кнопку не среди разметки активности, а внутри разметки самого фрагмента.

Нужно помнить, что в методе inflate() последний параметр должен иметь значение false в большинстве случаев.

FragmentManager

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

findFragmentById(int id) Находит фрагмент по идентификатору findFragmentByTag(String tag) Находит фрагмент по заданному тегу

Методы транзакции

Мы уже использовали некоторые методы класса FragmentTransaction. Познакомимся с ними поближе

add() Добавляет фрагмент к активности remove() Удаляет фрагмент из активности replace() Заменяет один фрагмент на другой hide() Прячет фрагмент (делает невидимым на экране) show() Выводит скрытый фрагмент на экран detach() (API 13) Отсоединяет фрагмент от графического интерфейса, но экземпляр класса сохраняется attach() (API 13) Присоединяет фрагмент, который был отсоединён методом detach()

Методы remove(), replace(), detach(), attach() не применимы к статичным фрагментам.

Перед началом транзакции нужно получить экземпляр FragmentTransaction через метод FragmentManager.beginTransaction(). Далее вызываются различные методы для управления фрагментами.

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

Аргументы фрагмента

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

  • Активность может создать фрагмент и установить аргументы для него
  • Активность может вызвать методы экземпляра фрагмента
  • Фрагмент может реализовать интерфейс, который будет использован в активности в виде слушателя
Читайте также:  Android debug bridge google

Фрагмент должен иметь только один пустой конструктор без аргументов. Но можно создать статический newInstance с аргументами через метод setArguments().

Доступ к аргументам можно получить в методе onCreate() фрагмента:

Динамически загружаем фрагмент в активность.

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

Вызываем метод в активности:

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

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

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

Чтобы добавить транзакцию в стек, вызовите метод FragmentTransaction.addToBackStack(String) перед завершением транзакции (commit). Строковый аргумент — опциональное имя для идентификации стека или null. Класс FragmentManager имеет метод popBackStack(), возвращающий предыдущее состояние стека по этому имени.

Если вы вызовете метод addToBackStack() при удалении или замещении фрагмента, то будут вызваны методы фрагмента onPause(), onStop(), onDestroyView().

Когда пользователь нажимает на кнопку возврата, то вызываются методы фрагмента onCreateView(), onActivityCreated(), onStart() и onResume().

Рассмотрим пример реагирования на кнопку Back в фрагменте без использования стека. Активность имеет метод onBackPressed(), который реагирует на нажатие кнопки. Мы можем в этом методе сослаться на нужный фрагмент и вызвать метод фрагмента.

Теперь в классе фрагмента прописываем метод с нужным кодом.

Более желательным вариантом является использование интерфейсов. В некоторых примерах с фрагментами такой приём используется.

Интеграция Action Bar/Options Menu

Фрагменты могут добавлять свои элементы в панель действий или меню активности. Сначала вы должны вызвать метод Fragment.setHasOptionsMenu() в методе фрагмента onCreate(). Затем нужно задать настройки для методов фрагмента onCreateOptionsMenu() и onOptionsItemSelected(), а также при необходимости для методов onPrepareOptionsMenu(), onOptionsMenuClosed(), onDestroyOptionsMenu(). Работа методов фрагмента ничем не отличается от аналогичных методов для активности.

В активности, которая содержит фрагмент, данные методы автоматически сработают.

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

Код для активности:

Код для фрагмента:

Связь между фрагментом и активностью

Экземпляр фрагмента связан с активностью. Активность может вызывать методы фрагмента через ссылку на объект фрагмента. Доступ к фрагменту можно получить через методы findFragmentById() или findFragmentByTag().

Источник

Fragment (Фрагменты). Часть вторая

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

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

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

Заворачиваем в фрагменты

Фрагмент, как и активность, состоит из разметки и класса. Сначала займёмся разметкой.

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

Создадим две отдельные разметки и скопируем нужные части из общей разметки в разметки для фрагментов. Делаем щелчок правой кнопкой мыши на папке res/layout и выбираем New | Layout Resource File.

Создаём новый файл fragment1.xml и размещаем верхнюю часть кода:

Также поступаем со вторым фрагментом — создаём новый файл fragment2.xml и в него копируем код из нижней части кода.

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

Пока мы создали разметки для будущих фрагментов. Теперь нужно создать отдельные классы для двух фрагментов. Для начала укажем, что наш класс должен наследоваться от класса Fragment. Не копируйте, а пишите код самостоятельно. Я создаю классы вручную с нуля, можно также воспользоваться готовым шаблоном Fragment (Blank), которым пользовались в первой части.

Следите, чтобы импортировался класс androidx.fragment.app.Fragment, а не устаревшие классы.

Самостоятельно создайте класс для второго фрагмента Fragment2 по такому же принципу.

Читайте также:  Что такое хард ресет android

Настало время подключить разметки к фрагментам. В активностях мы подключали разметку в методе onCreate() через метод setContentView(). В фрагментах метод onCreate() служит для других задач. А для подключения разметки используется отдельный метод onCreateView().

Чтобы долго не искать нужный нам метод, просто вводите на клавиатуре первую и заглавные буквы метода — ocv . Такой комбинации соответствует только один метод, который нам и нужен. Нажимаем кнопку OK и в код фрагмента будет вставлен следующий шаблон:

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

В Java-варианте код разбит на две части. Сначала мы получаем объект View, а затем уже его возвращаем в методе (не обязательно).

Скопируйте код метода onCreateView() и вставьте его в код класса Fragment2, не забыв указать разметку R.layout.fragment2.

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

Возвращаемся к главной разметке активности. Смело удаляем все элементы с экрана, чтобы остался только корневой элемент LinearLayout.

В старых версиях студии на панели инструментов был готовый компонент .

Сейчас компонент убрали, поэтому напишем код вручную в режиме Code.

Обратите внимание на атрибут tools:layout=»@layout/fragmentX» у тегов fragment, они помогут отобразить содержимое фрагментов в режиме дизайна.

Если сейчас запустим приложение, то тоже никаких изменений не увидим. Зачем тогда потратили столько времени на создание фрагментов? Непонятно.

А, я понял. Можно теперь писать в резюме про свои умения: использую фрагменты.

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

Мы знаем, что можно создать отдельную папку res/layout-land (перечитайте урок Ориентация) и разместить там разметку для такого случая.

Скопируем файл activity_main.xml и вставим его в новую папку.

Скорее всего вы не видите созданную папку, но она есть! Переключитесь в режим Project и заново откройте структуру проекта, найдя нужную папку. В дальнейшем оставайтесь в этом режиме для данного урока.

Изменим разметку фрагмента для альбомной ориентации.

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

Но это мы могли сделать и без фрагментов. Зачем же они нужны? Пока версия с лишней строчкой в резюме остаётся основной — чтобы работодатель уважал за прогрессивный стиль.

Хотя небольшое удобство есть. Благодаря модульности, мы поменяли разметку только у фрагментов, а то, что было внутри фрагментов (кнопки, текстовые блоки и т.д.), мы не трогали.

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

Давайте подключим поддержку планшетов. Создадим новую папку layout-sw600dp и скопируем в него файл из папки layout-land. Идентификатор sw600 говорит о минимальной ширине 600dp, что соответствует 7-дюймовым планшетам в альбомной ориентации. Существуют и другие варианты для планшетов с большими размерами.

Тут возникает небольшая проблема — если нам понадобится что-то изменить в разметке для альбомной ориентации, то придётся редактировать файлы во всех папках. Но есть выход из этой ситуации — использовать псевдонимы.

Мы можем создать одну копию разметки и указать, чтобы её использовали все нужные размеры устройств.

Делаем следующее. В папке layout-land переименовываем файл activity_main.xml в activity_main_wide.xml (Refactor | Rename) и перемещаем файл в папку layout. Пустую папку layout-land можно удалить.

Теперь создайте новую папку res/values-land. В созданной папке создаём новый файл refs.xml (имя не имеет значения, но так принято).

Этот файл говорит, что в альбомной ориентации вместо ресурса activity_main следует подключать ресурс layout/activity_main_wide. Можете запустить приложение и убедиться, что ничего не изменилось.

Если у вас будет поддержка альбомных ориентаций для разных размеров планшетов, то просто копируйте файл refs.xml в папки типа values-720dp_land и др.

Теперь вы можете вносить изменения в одном файле activity_main_wide.xml, а не по отдельности в каждом файле.

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

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

Читайте также:  Как поменять язык хром андроид

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

Подключаем кнопки в методе onViewCreated. Код будет похож на код, который мы обычно используем в методе onCreate() у активности, только метод findViewById() будет относиться уже не к классу Activity (обычно, мы опускали это), а к корневому элементу разметки фрагмента, в нашем случае view/rootView. В Java-варианте используется старый пример до появления метода onViewCreated(). Раньше приходилось писать код в onCreateView().

Для начала просто выведем сообщение, что кнопка нажата.

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

Каждой кнопке соответствует свой индекс от 1 до 3.

Фрагмент всегда может узнать, в какой активности он находится, через метод getActivity(). В методе makeText() мы уже воспользовались данным методом, так как в фрагментах нет метода getApplicationContext().

Перепишем код для щелчка кнопки, чтобы узнать индекс нажатой кнопки.

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

Для этой цели используются интерфейсы.

Открываем код первого фрагмента Fragment1 и объявляем интерфейс с единственным методом до объявления самого класса Fragment1:

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

У нас интерфейс будет использовать класс активности.

Переходим в класс активности и добавляем интерфейс OnSelectedButtonListener, который следует реализовать.

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

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

Но сначала подготовим второй фрагмент к работе. Объявим ссылки на компоненты, которые есть в разметке второго фрагмента. А также загрузим массив строк из ресурсов, который будем использовать для описания котов. Не забывайте, что в Java-варианте используется устаревший код.

Массив задаём в ресурсах (файл res/values/strings.xml). Так как первый элемент массива идёт под индексом 0, то добавим нейтральный текст:

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

Осталось только получить информацию от активности (не от фрагмента) об индексе.

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

Активность получает доступ к своим фрагментам через специальный менеджер фрагментов (коты называют его манагером). Менеджер есть у любой активности, поэтому мы его не создаём через конструкцию new FragmentManager, а получаем через метод getSupportFragmentManager().

Менеджер фрагментов держит в руках все нити управления над своими фрагментами. Найти нужный фрагмент можно по идентификатору через метод FragmentManager.findFragmentById(), который похож на метод findViewById() для получения идентификатора кнопки, метки и т.д. У менеджера есть ещё один метод для поиска фрагмента по тегу findFragmentByTag().

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

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

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

Но теперь в методе onClick() мы можем получить доступ к слушателю активности.

По цепочке мы передаём информацию от первого фрагмента в активность, а затем активность передаёт информацию во второй фрагмент.

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

Запустите проект и проверьте на работоспособность. Для данного случая мы пока не получили никаких преимуществ в использовании фрагментов. Но сейчас главное для вас — понять основные принципы создания и взаимодействия фрагментов.

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

Источник

Оцените статью