что такое fragment в android
Фрагменты
Существует два основных подхода в использовании фрагментов.
Первый способ основан на замещении родительского контейнера. Создаётся стандартная разметка и в том месте, где будут использоваться фрагменты, размещается контейнер, например, FrameLayout. В коде контейнер замещается фрагментом. При использовании подобного сценария в разметке не используется тег fragment, так как его нельзя менять динамически. Также вам придётся обновлять ActionBar, если он зависит от фрагмента. Здесь показан такой пример.
Второй подход является наиболее гибким и в целом предпочтительным способом использования фрагментов. Активность проверяет в каком режиме (свои размеры) он запущен и использует разную разметку из ресурсов. Графически это выглядит следующим образом.
Основные классы
Сами фрагменты наследуются от androidx.fragment.app.Fragment. Существует подклассы фрагментов: ListFragment, DialogFragment, PreferenceFragment, WebViewFragment и др. Не исключено, что число классов будет увеличиваться, например, появился ещё один класс MapFragment.
Как в любом офисе, спецманагер не делает работу своими руками, а использует помощников. Например, для транзакций (добавление, удаление, замена) используется класс-помощник android.app.FragmentTransaction.
Для сравнения приведу названия классов из библиотеки совместимости:
Как видите, разница в одном классе, который я привёл первым. Он используется вместо стандартного Activity, чтобы система поняла, что придётся работать с фрагментами. На данный момент студия создаёт проект на основе ActionBarActivity, который является подклассом FragmentActivity.
В одном приложении нельзя использовать новые фрагменты и фрагменты из библиотеки совместимости.
В 2018 году Гугл объявила фрагменты из пакета androd.app устаревшими. Заменяйте везде на версию из библиотеки совместимости. В 2020 году уже используют пакет androidx.fragment.app.
В версии Support Library 27.1.0 появились новые методы requireActivity() и requireContext(), которые пригодятся при написании кода, когда требуется наличие активности и нужно избежать ошибки на null.
Общий алгоритм работы с фрагментами будет следующим:
У каждого фрагмента должен быть свой класс. Класс наследуется от класса Fragment или схожих классов, о которых говорилось выше. Это похоже на создание новой активности или нового компонента.
Разметку для фрагмента можно создать программно или декларативно через 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().
Аргументы фрагмента
Фрагменты должны сохранять свою модульность и не должны общаться друг с другом напрямую. Если один фрагмент хочет докопаться до другого, он должен сообщить об этом своему менеджеру активности, а он уже передаст просьбу другому фрагменту. И наоборот. Это сделано специально для того, чтобы было понятно, что менеджер тут главный и он не зря зарплату получает. Есть три основных способа общения фрагмента с активностью.
Фрагмент должен иметь только один пустой конструктор без аргументов. Но можно создать статический newInstance с аргументами через метод setArguments().
Доступ к аргументам можно получить в методе onCreate() фрагмента:
Динамически загружаем фрагмент в активность.
Вызываем метод в активности:
Если фрагмент должен сообщить о своих действиях активности, то следует реализовать интерфейс.
Управление стеком фрагментов
Фрагменты, как и активности, могут управляться кнопкой Back. Вы можете добавить несколько фрагментов, а потом через кнопку Back вернуться к первому фрагменту. Если в стеке не останется ни одного фрагмента, то следующее нажатие кнопки закроет активность.
Если вы вызовете метод addToBackStack() при удалении или замещении фрагмента, то будут вызваны методы фрагмента onPause(), onStop(), onDestroyView().
Когда пользователь нажимает на кнопку возврата, то вызываются методы фрагмента onCreateView(), onActivityCreated(), onStart() и onResume().
Рассмотрим пример реагирования на кнопку Back в фрагменте без использования стека. Активность имеет метод onBackPressed(), который реагирует на нажатие кнопки. Мы можем в этом методе сослаться на нужный фрагмент и вызвать метод фрагмента.
Теперь в классе фрагмента прописываем метод с нужным кодом.
Более желательным вариантом является использование интерфейсов. В некоторых примерах с фрагментами такой приём используется.
Интеграция Action Bar/Options Menu
Фрагменты могут добавлять свои элементы в панель действий или меню активности. Сначала вы должны вызвать метод Fragment.setHasOptionsMenu() в методе фрагмента onCreate(). Затем нужно задать настройки для методов фрагмента onCreateOptionsMenu() и onOptionsItemSelected(), а также при необходимости для методов onPrepareOptionsMenu(), onOptionsMenuClosed(), onDestroyOptionsMenu(). Работа методов фрагмента ничем не отличается от аналогичных методов для активности.
В активности, которая содержит фрагмент, данные методы автоматически сработают.
Если активность содержит собственные элементы панели действий или меню, то следует позаботиться, чтобы они не мешали вызовам методам фрагментов.
Код для активности:
Связь между фрагментом и активностью
Экземпляр фрагмента связан с активностью. Активность может вызывать методы фрагмента через ссылку на объект фрагмента. Доступ к фрагменту можно получить через методы findFragmentById() или findFragmentByTag().
Android Fragment. Что это?
В этом уроке, я хочу объяснить, что такое Fragment и почему его нужно использовать. Мы рассмотрим немного теории, а также практический пример применения Fragments.
Шаг 0. Теория
Так как определение не совсем понятно, я решил сказать своими словами его.
Fragment (Фрагмент) – по сути это подобие Activity, которое мы можем подключать в разные части приложения. Но одно Activity может содержать несколько fragment.
Фрагменты появились в API 11 (Android 3.0) для поддержки на более старых версиях был доработан Android Support library.
Также важно понимать, что фрагменты – это не замена активности, они не могут существовать сами по себе, а только вместе с Activity. Поэтому в AndroidManifest регистрировать Fragments не нужно.
Шаг 1. Создаем проект
Давайте создадим простой проект, пока без использования Fragment-ов. Создаем Android Gradle Project:
Теперь создадим Activity в пакете com.myfragmentexam.app назовем его MainActivity:
Для этого activity нам нужно создать layout, создаем в res/layout новый layout и называем его main_layout:
Как видите в 33-й строке мы указываем на ресурс изображения, для этого вам нужно в res/drawable добавить следующее изображение:
Начиная с 9-й строки мы регистрируем Activity в контексте.
Теперь запустим. Мы должны увидеть следующее:
Пока это простое приложение, которое не выполняет никакой логики и не имеет фрагментов.
Шаг 2. Делим на части
Особенность Fragment-ов в том, что вы можете разбить внешний вид на блоки и потом подключать их для отображения на разных устройствах по своему (смартфон/планшет). Также мы получим возможность переиспользования блоков(Fragments).
Первыйм делом вынесем кнопки в отдельный фрагмент, в res/layout создаем новый layout и называем его button_fragment:
Выглядеть это будет так:
Теперь вынесем CheckBox-сы в отдельный layout назовем его checkbox_fragment:
Выглядеть это будет так:
И вынесем картинку в layout назвав его image_fragment:
Выглядеть это будет так:
Шаг 3. Создаем Fragments
Мы поделили наш первоначальный вид на части, теперь давайте создадим на их основе фрагменты.
Посмотрите на структуру классов ниже и на её основе создайте пакет fragments и в нем классы классы ButtonFragment, CheckBoxFragment, ImageFragment.
Причина этого то что фрагменты в вспомогательном пакете более лучше поддерживаются и если сравнивать со стандартным, то он не имеет тех багов, которые имеет стандартный пакет( android.app ).
В Activity мы подключали layout в методе onCreate() через метод setContentView(). Но в фрагментах метод onCreate() используется немного для других целей. Поэтому для подключения layout используется отдельный метод onCreateView().
Давайте начнем с ButtonFragment :
Теперь детальней рассмотрим что же мы тут делаем.
LayoutInflater – позволяет построить нужный макет, считывая информацию из указанного XML-файла.
Обратите внимание на то что 16-й строке в метод inflate() мы передаем 3 параметра разметку нашего фрагмента, контейнер, и значение (true |false), которое указывает на возможность подключения фрагментов в Activity через контейнер. Мы указали false так как сами создаем блоки для фрагментов.
Теперь по аналогии создадим реализацию для фрагмента CheckBoxFragment:
И для последнего ImageFragment:
Шаг 4. Собираем все в кучу
Мы создали пачку фрагментов, но что с ними делать? Их мы теперь можем подключать в те места куда нам нужно. Для примера мы переделаем MainActivity и main_layout под фрагменты.
Для начало давайте модифицируем MainActivity:
Обратите на строку 6, как видите мы наследуемся не просто от Activity, а от FragmentActivity это нужно делать когда вы используете Fragment с пакета android.support.v4.app.
Теперь модифицируем layout для нашего Activity, а именно main_layout:
Обратите внимание на выделенные строки, тут мы указываем какой фрагмент подключать.
Но теперь в Intellij IDEA режим Preview работает не корректно:
Для того чтобы это исправить нам нужно немного модифицировать main_layout:
На выделенных строках видно изменения, таким образом мы указали фрагментам на нашем layout какие layout они отображают.
И теперь режим Preview работает корректно:
После этого, можно запустить и проверить. Вы должны увидеть тоже что показано на Preview.
Шаг 5. Добавляем альбомный вид
Для этого создаем в res папке layout-land и копируем в в него все содержимое папки layout.
После этого немного изменим расположение фрагментов на main_layout. Обратите внимание, что мы будем менять расположение фрагментов, а не их компонентов, что довольно таки удобно.
Теперь запускаем и смотрим на результат:
Теперь у нас есть вид как для смартфонов так и для планшетов.
Полный список
— используем фрагменты
— разбираемся в их lifecycle
Фрагменты – одно из главных новшеств Android 3. Можно рассматривать их как мини-Activity, которые располагаются в основном Activity и имеют свой lifecycle, немного отличающийся от обычного Activity. В этом уроке разместим пару фрагментов в Activity и разберемся в lifecycle-методах.
В 4-й версии фрагменты никуда не делись, а AVD для 3-й версии какие-то совсем суровые и тяжелые, поэтому для разработки и тестирования будем использовать версию Android 4.1 (API 16). Если у вас нет такой версии в списке доступных, то открывайте Window > Android SDK Manager и скачивайте ее там.
Project name: P1041_FragmentLifecycle
Build Target: Android 4.1
Application name: FragmentLifecycle
Package name: ru.startandroid.develop.p1041fragmentlifecycle
Create Activity: MainActivity
В strings.xml добавим пару строк:
Создадим пару фрагментов. Для этого нам необходимо создать для них layout-файлы и классы с предком android.app.Fragment.
Создаем layout-файлы, как обычно.
Фрагменты будут содержать TextView с текстом, и мы сделали цветной фон для наглядности.
Теперь классы. Если Activity наследует android.app.Activity, то фрагменты наследуют android.app.Fragment.
Заполняем код. Fragment1.java:
В обоих фрагментах просто выводим в лог вызовы всех lifecycle-методов. Чуть дальше рассмотрим эти методы подробнее.
В методе onCreateView система спрашивает у нас, что ей отображать внутри фрагмента. Мы сообщаем системе, что хотим видеть во фрагменте содержимое соответствующего layout-файла. Для этого мы сами создаем View с помощью inflater и отдаем его системе. Т.е. по смыслу это аналог метода setContentView, который мы вызываем в Activity. Только здесь нам приходится самим создавать View, а не просто передавать идентификатор layout-файла.
Все (layout и классы) для фрагментов готово. Можем поместить их в основной layout-файл Activity. Открываем main.xml, делаем корневым горизонтальный LinearLayout и помещаем в него пару элементов Fragment (вкладка Layouts). При этом появится диалог, в котором надо будет указать какой класс используется для фрагмента.
Указываем для первого класс Fragment1, а для второго Fragment2.
Выровняем фрагменты по ширине с помощью веса. В итоге должен получиться такой main.xml:
В MainActivity.java также добавляем запись в лог всех lifecycle методов:
Все сохраняем. Далее, я сначала запустил эмулятор через AVD, повернул его в горизонтальную ориентацию, затем запустил приложение. Но, в принципе, можно все делать и в вертикальной ориентации. Скрин будет чуть другой, а логи те же.
Все как и заказывали. В горизонтальном LinearLayout размещены пара фрагментов. Содержимое фрагментов взято из layout-файлов fragment1 и fragment2.
Fragment1 onAttach
Fragment1 onCreate
Fragment1 onCreateView
Fragment2 onAttach
Fragment2 onCreate
Fragment2 onCreateView
MainActivity onCreate
Fragment1 onActivityCreated
Fragment2 onActivityCreated
MainActivity onStart
Fragment1 onStart
Fragment2 onStart
MainActivity onResume
Fragment1 onResume
Fragment2 onResume
Первым делом для фрагментов вызываются методы:
onAttach – фрагмент прикреплен к Activity и получает ссылку на него. В дальнейшем мы всегда можем получить ссылку на Activity, вызвав метод getActivity().
onCreateView – здесь вы создаете View, который будет содержимым фрагмента, и отдаете его системе
Далее срабатывают метод Activity – onCreate, после него метод фрагментов onActivityCreated – сообщает фрагменту о том, что Activity создано и можно работать с UI-элементами
Далее метод Activity – onStart, после него onStart – аналогичен методу Activity, фрагмент виден пользователю
Жмем кнопку назад – закрываем приложение:
Fragment1 onPause
Fragment2 onPause
MainActivity onPause
Fragment1 onStop
Fragment2 onStop
MainActivity onStop
Fragment1 onDestroyView
Fragment1 onDestroy
Fragment1 onDetach
Fragment2 onDestroyView
Fragment2 onDestroy
Fragment2 onDetach
MainActivity onDestroy
Сначала для фрагментов и Activity вызываются методы onPause и onStop. Это значит, что фрагменты и Activity более недоступны для взаимодействия, а потом не видны пользователю.
Затем для фрагментов вызываются три метода по уничтожению:
onDestroyView – сообщает нам, что View, которое мы создавали в onCreateView, более недоступно
onDestroy – аналог метода onDestroy у Activity
onDetach – фрагмент отсоединен от Activity
И в конце вызывается метод onDestroy для Activity.
Т.е. основные lifecycle методы схожи для Activity и фрагмента. Но есть и некоторые различия, связанные с привязкой фрагмента к Activity.
Фрагменты, так же как и Activity могут сохранять данные при своем пересоздании, например при смене экрана. Для записи используется метод onSaveInstanceState. А прочесть данные можно из Bundle в методах onCreate, onCreateView или onActivityCreated.
А чтобы при пересоздании сохранить сам объект класса Fragment, используйте метод setRetainInstance. Если передать в него true, то при пересоздании фрагмента не будут вызваны методы onDestroy и onCreate, и не будет создан новый экземпляр класса Fragment.
На следующем уроке:
— динамически работаем с фрагментами
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Фрагменты
Введение во фрагменты
Фрагмент представляет кусочек визуального интерфейса приложения, который может использоваться повторно и многократно. У фрагмента может быть собственный файл layout, у фрагментов есть свой собственный жизненный цикл. Фрагмент существует в контексте activity и имеет свой жизненный цикл, вне activity обособлено он существовать не может. Каждая activity может иметь несколько фрагментов.
В ее начало добавим строку
То есть в моем случае получится
Итак, добавим в папку res/layout новый файл fragment_content.xml и определим в нем следующий код:
Фрагменты содержат те же элементы управления, что и activity. В частности, здесь определены кнопка и текстовое поле, которые будут составлять интерфейс фрагмента.
Чтобы указать, что фрагмент будет использовать определенный xml-файл layout, идентификатор ресурса layout передается в вызов конструктора родительского класса (то есть класса Fragment).
Весь проект будет выглядеть следующим образом:
Добавление фрагмента в Activity
Код класса MainActivity остается тем же, что и при создании проекта:
Если мы запустим приложение, то мы увидим фактически тот же самый интерфейс, который мы могли бы сделать и через activity, только в данном случае интерфейс будет определен во фрагменте:
Стоит отметить, что Android Studio представляет готовый шаблон для добавления фрагмента. Собственно воспользуемся этим способом.
Данный шаблон предложить указать класс фрагмента и название файла связанного с ним класса разметки интерфейса.
Добавление логики к фрагменту
Фрагмент определяет кнопку. Теперь добавим к этой кнопки некоторое действие. Для этого изменим класс ContentFragment:
Здесь переопределен метод onViewCreated класса Fragment, который вызывается после создания объекта View для визуального интерфейса, который представляет данный фрагмент. Созданный объект View передается в качестве первого параметра. И далее мы можем получить конкретные элементы управления в рамках этого объекта View, в частности, TextView и Button, и выполнить с ними некоторые действия. В данном случае в обработчике нажатия кнопки в текстовом поле выводится текущая дата.
Добавление фрагмента в коде
Кроме определения фрагмента в xaml-файле интерфейса мы можем добавить его динамически в activity.
Для этого изменим файл activity_main.xml :
И также изменим класс MainActivity :
Итоговый результат такого добавления фрагмента будет тем же, что и при явном определении фрагмента через элемент FragmentContainerView в разметке интерфейса.