Fragment что это android

Обновлено: 18.05.2024

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

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

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

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

Есть три основных класса:
android.app.Fragment — от него, собственно говоря. и будут наследоваться наши фрагменты
android.app.FragmentManager — с помощью экземпляра этого класса происходит все взаимодействие между фрагментами
android.app.FragmentTransaction — ну и этот класс, как понятно по названию, нужен для совершения транзакций.
В настоящее время появляются разновидности класса Fragment, для решения определенных задач — ListFragment, PreferenceFragment и др.

Основы работы с fragment'ами

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

Чтобы получить это View из любого места фрагмента достаточно вызвать getView()

Фрагмент мы создали, но хотелось бы поместить его на экран. Для этого нам нужно получить экземпляр FragmentManager и совершить нужную нам транзакцию.
Сначала нужно узнать что мы с фрагментом можем сделать:
add() — добавление фрагмента
remove() — удаление фрагмента
replace() — замена фрагмента
hide() — делает фрагмент невидимым
show() — отображает фрагмент

Так же для того чтобы добавлять наши транзакции в стек, как это происходит по умолчанию с активностями, можно использовать addToBackStack(String), а чтобы вернуть предыдущее состояние стэка нужно вызвать метод popBackStack().

Добавим фрагмент на экран:

Как связать activity и fragment?

Чтобы вызывать методы активити, достаточно получить его экземпляр через метод getActivity()

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

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

  • сериализовать объект
  • передать объект в контейнере Parcel, переопределив методы Parcable
  • кое-где видел, что для создания фрагмента через переопределенный конструктор, создают статический фабричный метод

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

Здесь мы реализовали интерфейс Parcelable в классе, который хотим передавать между фрагментами.
Чтобы передать его во фрагмент нужно сделать следующее:

Дальше нужно всего лишь получить переданный нами объект в методе onCreateView() нового фрагмента:


UPD. исправил получение объекта obj из getArguments(), спасибо firexel

Анимация фрагментов

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

Чтобы создать свою анимацию добавления и удаления фрагмента? нужно создать два файла в директории res/animator, один из них будет служить для анимации добавления, второй для удаления

Приведу пример одного из них:

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

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


В этом уроке, я хочу объяснить, что такое Fragment и почему его нужно использовать. Мы рассмотрим немного теории, а также практический пример применения Fragments.

Представьте себе пазл, который вы можете сложить и получить общий вид картинки, именно так можно представить Fragment . Или же если вы работали разработкой сайтов, а именно PHP то вам знакома возможность подключать общие части сайта например меню как отдельного блока с помощью include .

Шаг 0. Теория

Так как определение не совсем понятно, я решил сказать своими словами его.


Фрагменты появились в API 11 (Android 3.0) для поддержки на более старых версиях был доработан Android Support library.

Шаг 1. Создаем проект

Давайте создадим простой проект, пока без использования Fragment-ов. Создаем Android Gradle Project:


Теперь создадим Activity в пакете com.myfragmentexam.app назовем его MainActivity:

Для этого activity нам нужно создать layout, создаем в res/layout новый layout и называем его main_layout:

Как видите в 33-й строке мы указываем на ресурс изображения, для этого вам нужно в res/drawable добавить следующее изображение:

Последним шагом нужно зарегистрировать activity MainActivity в AndroidManifest . xml :

Начиная с 9-й строки мы регистрируем Activity в контексте.

Теперь запустим. Мы должны увидеть следующее:


Пока это простое приложение, которое не выполняет никакой логики и не имеет фрагментов.

Шаг 2. Делим на части

Особенность Fragment-ов в том, что вы можете разбить внешний вид на блоки и потом подключать их для отображения на разных устройствах по своему (смартфон/планшет). Также мы получим возможность переиспользования блоков(Fragments).

Первыйм делом вынесем кнопки в отдельный фрагмент, в res/layout создаем новый layout и называем его button_fragment:

Выглядеть это будет так:


Теперь вынесем CheckBox-сы в отдельный layout назовем его checkbox_fragment:

Выглядеть это будет так:


И вынесем картинку в layout назвав его image_fragment:

Выглядеть это будет так:


Шаг 3. Создаем Fragments

Мы поделили наш первоначальный вид на части, теперь давайте создадим на их основе фрагменты.

Посмотрите на структуру классов ниже и на её основе создайте пакет fragments и в нем классы классы ButtonFragment, CheckBoxFragment, ImageFragment.


Для того чтобы все эти классы стали фрагментами их нужно унаследовать от класса Fragment . Обратите внимание что класс фрагмент мы будем использовать не со стандартной библиотеки android . app , а с android . support .v4. app .

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

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

Давайте начнем с ButtonFragment :

Теперь детальней рассмотрим что же мы тут делаем.

Обратите внимание на то что 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 работает не корректно:


На выделенных строках видно изменения, таким образом мы указали фрагментам на нашем layout какие layout они отображают.

И теперь режим Preview работает корректно:


После этого, можно запустить и проверить. Вы должны увидеть тоже что показано на Preview.

Шаг 5. Добавляем альбомный вид

Для этого создаем в res папке layout-land и копируем в в него все содержимое папки layout.

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

Фрагменты – одно из главных новшеств Android 3. Можно рассматривать их как мини-Activity, которые располагаются в основном Activity и имеют свой lifecycle, немного отличающийся от обычного Activity. В этом уроке разместим пару фрагментов в Activity и разберемся в lifecycle-методах.

В 4-й версии фрагменты никуда не делись, а AVD для 3-й версии какие-то совсем суровые и тяжелые, поэтому для разработки и тестирования будем использовать версию Android 4.1 (API 16). Если у вас нет такой версии в списке доступных, то открывайте Window > Android SDK Manager и скачивайте ее там.

Создадим AVD 4.1


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-файлы, как обычно.

fragment1.xml:

fragment2.xml:

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

Теперь классы. Если Activity наследует android.app.Activity, то фрагменты наследуют android.app.Fragment.

Создаем Fragment1:


и Fragment2


Заполняем код. Fragment1.java:

Fragment2.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().

onCreate - это аналог метода onCreate у Activity, но здесь мы пока не имеем доступа к UI-элементам

onCreateView – здесь вы создаете View, который будет содержимым фрагмента, и отдаете его системе

Далее срабатывают метод Activity – onCreate, после него метод фрагментов onActivityCreated – сообщает фрагменту о том, что Activity создано и можно работать с UI-элементами

Далее метод Activity – onStart, после него onStart – аналогичен методу Activity, фрагмент виден пользователю

Далее метод Activity – onResume, после него onResume - аналогичен методу 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.

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

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

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

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

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

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

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

Фрагменты в Android

Для начала работы с фрагментами создадим новый проект с пустой MainActivity. И вначале создадим первый фрагмент. Но сразу стоит отметить, что не вся функциональность фрагментов по умолчанию может быть доступна в проекте, поскольку располагается в отдельной библиотеке - AndroidX Fragment library . И вначале необходимо подключить к проекту эту библиотеку в файле build.gradle .

Подключение фрагментов и AndroidX Fragment library в Android и Java

Найдем в нем секцию dependencies , которая выглядит по умолчанию примерно так:

В ее начало добавим строку

То есть в моем случае получится

Подключение фрагментов и AndroidX Fragment library в Android и Java и Gradle

И затем нажмем на появившуюся ссылку Sync Now .

Фактически фрагмент - это обычный класс Java, который наследуется от класса Fragment . Однако как и класс Activity, фрагмент может использовать xml-файлы layout для определения графического интерфейса. И таким образом, мы можем добавить по отдельности класс Java, который представляет фрагмент, и файл xml для хранения в нем разметки интерфейса, который будет использовать фрагмент.

Итак, добавим в папку res/layout новый файл fragment_content.xml и определим в нем следующий код:

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

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

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

Чтобы указать, что фрагмент будет использовать определенный xml-файл layout, идентификатор ресурса layout передается в вызов конструктора родительского класса (то есть класса Fragment).

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

Фрагменты в Android Studio и Java

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

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

Для добавления фрамента применяется элемент FragmentContainerView . По сути FragmentContainerView представляет объект View, который расширяет класс FrameLayout и предназначен специально для работы с фрагментами. Собственно кроме фрагментов он больше ничего содержать не может.

Его атрибут android:name указывает на имя класса фрагмента, который будет использоваться. В моем случае полное имя класса фрагмента с учетов пакета com.example.fragmentapp.ContentFragment .

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

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

Создание фрагмента для Android и Java

Стоит отметить, что Android Studio представляет готовый шаблон для добавления фрагмента. Собственно воспользуемся этим способом.

Для этого нажмем на папку, где находится класс MainActivity , правой кнопкой мыши и в появившемся меню выберем New -> Fragment -> Fragment(Blank) :

Добавление фрагмента в Android Studio

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

Добавление фрагмента в Android Studio и Java

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

Фрагмент определяет кнопку. Теперь добавим к этой кнопки некоторое действие. Для этого изменим класс ContentFragment:

Здесь переопределен метод onViewCreated класса Fragment, который вызывается после создания объекта View для визуального интерфейса, который представляет данный фрагмент. Созданный объект View передается в качестве первого параметра. И далее мы можем получить конкретные элементы управления в рамках этого объекта View, в частности, TextView и Button, и выполнить с ними некоторые действия. В данном случае в обработчике нажатия кнопки в текстовом поле выводится текущая дата.

Добавление фрагмента в AndroidX Fragment Library и Java

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

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

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

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

Метод getSupportFragmentManager() возвращает объект FragmentManager , который управляет фрагментами.

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

FragmentTransaction выполняет два метода: add() и commit(). Метод add() добавляет фрагмент: add(R.id.fragment_container_view, new ContentFragment()) - первым аргументом передается ресурс разметки, в который надо добавить фрагмент (это определенный в activity_main.xml элемент androidx.fragment.app.FragmentContainerView ). И метод commit() подтвержает и завершает операцию добавления.

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

Читайте также: