Fragment (Фрагменты)
Фрагменты немного пугают новичков. Постараюсь объяснить как можно проще, чтобы отдельные фрагменты пазла сложились в единую картину.
Зачем?
Создатели операционной системы Android оказались недальновидными разработчиками. Не посоветовавшись с котами, они разработали систему под маленькие экраны телефонов. Но котам было неудобно пользоваться такими экранами, и тогда придумали планшеты.
Старые программы прекрасно на них запускались, но обнаружилось несколько недостатков. На больших экранах интерфейс выглядел не слишком элегантно, появились большие пустые пространства. И тогда возникла идея объединить два отдельных экрана из смартфона в один экран на планшете. Это самый классический пример применения фрагмента. По сути, это костыль. Возможно, если бы сразу подумали головой, то придумали бы более элегантное решение. Но теперь поздно пить «Боржоми», будем использовать предложенную концепцию.
Фрагменты были представлены в API 11 (Android 3.0), но в целях совместимости была написана специальная библиотека Android Support library для старых устройств. Долгое время существовало два класса Fragment: для новых устройств и для старых устройств. Названия методов и классов были очень похожи, и разработчики часто путались, смешивая в одном проекте два разных несовместимых класса. Спустя некоторое время решили отказаться от странного разделения, класс для новых устройств признали устаревшим, а класс из библиотеки поддержки старых устройств сменил своё полное имя и вошёл в состав AndroidX.
При желании можно было продолжить писать приложения в старом стиле, отслеживая размеры экрана. Но такой код получится слишком сложным. Пришлось бы писать один код для переключения от одной активности к другой при использовании смартфона и другой код, когда взаимодействие между объектами происходит на одном экране в планшете. Чтобы устранить это противоречие, были придуманы фрагменты. Хотя там тоже придётся писать много кода.
Несколько слов о том, как проще воспринимать фрагмент. Считайте, что фрагмент — это тот же компонент как Button, TextView или LinearLayout с дополнительными возможностями. Фрагмент, как и кнопку, нужно поместить на экран активности. Но фрагмент является модульным компонентом и один и тот же фрагмент можно встроить в две разные активности. С кнопкой такой номер не пройдёт. Для каждой активности вы должны создать свою отдельную кнопку, даже если их нельзя будет отличить друг от друга.
Фрагмент также немного похож на активность. Но фрагменты — это не замена активности, они не существуют сами по себе, а только в составе активностей. Поэтому в манифесте прописывать их не нужно. Но в отличие от стандартной кнопки, для каждого фрагмента вам придётся создавать отдельный класс, как для активности.
В составе активности есть специальный менеджер фрагментов, который может контролировать все классы фрагментов и управлять ими. О нём позже.
Фрагменты являются строительным материалом для приложения. Вы можете в нужное время добавить новый фрагмент, удалить ненужный фрагмент или заменить один фрагмент на другой. Точно так же мы собираем пазл — подносим фрагмент кота в общую картину, иногда ошибаемся и тогда заменяем кусочек пазла на другой и т.д.
Фрагмент может иметь свою разметку, а может обойтись без неё. Также у фрагмента есть свой жизненный цикл, во многом совпадающий с жизненным циклом активности. Пожалуй, это единственное сходство с активностью.
Имеются специальные виды фрагментов, заточенные под определённые задачи — ListFragment, DialogFragment и другие, которые изучим в других уроках.
Есть два варианта использования фрагментов в приложении (при желании можно использовать сразу оба варианта). Первый вариант заключается в том, что вы в разметке сразу указываете фрагмент с помощью тега fragment, так же как и с другими компонентами.
Второй вариант использует динамическое подключение фрагмента. Принцип следующий — в разметку помещается макет из группы ViewGroup, который становится контейнером для фрагмента. Обычно, для этой цели используют FrameLayout, но это не обязательное условие. И в нужный момент фрагмент замещает контейнер и становится частью разметки.
Поначалу фрагменты кажутся неудобными, так как количество кода увеличивается. Но если с ними работать постоянно, то станет понятнее их принцип.
Реакция разработчиков на появление фрагментов противоречива. Кто-то активно использует их в своих проектах, а кто-то их не переносит и использует альтернативные варианты. Похоже, в стане Гугла также идёт борьба между двумя группами программистов. Фрагменты постоянно развиваются, меняются и дорабатываются.
Первое знакомство
Для первого знакомства создадим стандартный проект на базе Empty Activity и вручную добавим фрагмент. Итак, после создания проекта выбираем из контекстного меню пакета New | Fragment | Fragment (Blank). В диалоговом окне мастера назначаем имя для фрагмента.
На этом этапе создание нового фрагмента напоминает создание новой активности. Мы создаём новый класс и автоматически генерируется макет для него fragment_cat.xml. Единственное отличие, в манифест ничего не добавляется.
Откроем макет фрагмента и немного отредактируем содержимое, добавив центрирование по центру. Также можно было добавить ImageView с изображением кота. Но не будем пока ничего усложнять.
В res/values/strings.xml содержится ресурс hello_blank_fragment. Изменим текст на что-то понятное:
Приготовления закончены. Осталось добавить фрагмент в активность. Открываем activity_main.xml и добавляем новый элемент.
В элементе fragment в атрибуте android:name указываем полное имя класса фрагмента.
Можно запустить проект и увидеть фрагмент внутри активности.
Жизненный цикл фрагмента
У фрагмента есть жизненный цикл, как у активности. Но число методов цикла гораздо больше. Откроем теперь файла класса фрагмента CatFragment и добавим новый код (выделено жирным).
Чтобы увидеть связь между жизненными циклами фрагмента и активности, добавим логи и в активность.
Снова запускаем проект и смотрим логи.
У фрагментов есть жизненный цикл, который во многом совпадает с жизненным циклом активности, внутри которой они находятся.
Два отдельных фрагмента
Мы научились размещать один фрагмент на экране активности. Ничто не мешает разместить на экране несколько фрагментов, которые являются контейнерами для компонентов. Давайте совместим на одном экране два старых примера — «Hello Kitty» и «Счётчик ворон/котов».
Повторим шаги создания нового фрагмента через готовый шаблон Fragment (Blank) и создадим два новых фрагмента KittyFragment и CounterFragment.
Разметку для KittyFragment копируем из старого примера.
Код также копируем с небольшим отличием. Если в активности мы размещали основной код в методе onCreate(), то для фрагмента используем onViewCreated(). Код шаблона для экономии я опущу.
Для счётчика котов (ворон больше считать не будем) напишем продвинутую версию, которая позволить не только увеличивать показания счётчика, но и уменьшать. В конце концов коты имеют привычку гулять сами по себе и пропадают из поля нашего зрения.
Разметка для фрагмента counter_fragment.xml.
Код для класса CounterFragment. Здесь также мы добавляем весь код в onViewCreated().
Осталось разместить два фрагмента в activity_main.xml.
Запускаем проект и видим два приложения в одном. В верхней части мы здороваемся с котёнком, а в нижней считаем котов. Обратите внимание, что весь код сосредоточился в классах фрагментов, а в MainActivity нам вообще ничего не пришлось писать. Фрагменты работают независимо и мы можем писать код в разных модулях.
Источник
LayoutInflater
Однажды я пытался раскрыть вам тайное значение названия метода inflate() — слово inflate происходит от словосочетания in flat — в квартиру. Существует старинная традиция запускать в квартиру первым кота, который исследует все закоулки дома и заявляет о своём согласии жить в нём. Однако, мы сделаем вид, что поверили официальной версии, что по-английски inflate переводится как надувать, т.е. мы как бы надуваем данными из XML-файла объекты View.
Мы рассмотрим надувание компонентов применимо к LayoutInflater. Существует также класс MenuInflater, который использует тот же принцип.
Фрагменты тоже часто используют механизм надувания. Обратите внимание на сигнатуру метода onCreateView():
Сам механизм надувания прост и не требует особых объяснений, изучения достоин один аспект.
Вероятно, вам не раз приходилось писать/копировать такой код:
Даже у меня на сайте есть такие примеры. Недавно я заметил, что среда разработки ругается на последний параметр null.
Оказывается, это неправильно. Давайте изучать этот код.
Существует две версии метода inflate() для стандартных приложений.
В первом параметре указывается идентификатор ресурса разметки, который мы собираемся надуть. Во втором параметре указывается корневой компонент, к которому нужно присоединить надутые объекты. В третьем параметре (если он используется) указывается, нужно ли присоединять надутые объекты к корневому элементу.
LayoutInflater автоматически пытается присоединить надутые компоненты к корневому элементу. Иногда сама среда разработки может создать заготовку с использованием null, чтобы избежать возможного краха приложения. И разработчики используют предложенный вариант, хотя можно было использовать второй вариант метода с тремя параметрами.
Но давайте узнаем, чего мы лишаемся, когда используем null для корневого элемента.
Надувание компонентов часто применяется в адаптерах, когда разметка отдельного элемента списка задаётся в XML-файле, а затем динамически конвертируется в набор компонентов в методе getView():
Среда разработки заругалась. Исправим null на parent, который по названию вроде подходит под корневой элемент. Однако, запустив проект, мы получим ошибку. Йожкин кот, что мы наделали?!
Вспоминаем, что есть другая версия метода и исправляем на следующий вариант.
Пример работает, предупреждение не выводится. Но осадочек остался.
При использовании null среда разработки как-бы пытается сказать: «Я не знаю, какой элемент является родительским, поэтому прости, между нами всё кончено «, я буду использовать null.
Тогда зачем нам этот родительский элемент? На самом деле он может сыграть важную роль в различных ситуациях.
Проблемы могут возникнуть при использовании атрибутов android:layout_xxx в родительском элементе. В результате, не зная ничего о родителе, мы не можем использовать его LayoutParams и система будет игнорировать вашу разметку. А вы будете думать, что это баг.
Без LayoutParams родительский ViewGroup надует компоненты с настройками по умолчанию. И в большинстве случае это вполне работает.
Рассмотрим конкретный пример. Создадим разметку для отдельного элемента списка layout/list_item.xml.
Мы хотим использовать фиксированную высоту для каждого элемента и используем атрибут android:layout_height у родительского элемента.
Попробуем неправильный вариант.
В этом варианте игнорируется родитель и соответственно его настройки.
Результат на экране.
Теперь вам должна быть ясна разница между разными вариантами и правильный код для применения.
Но бывают случаи, когда использование null оправданно.
Одним из таких примеров является использование разметки в AlertDialog. Допустим, у нас есть код.
AlertDialog.Builder не нуждается в родительском элементе для построения диалогового окна.
Рассмотрим случаи, когда нужен true. Допустим, у нас есть отдельная разметка кнопки.
Мы можем программно присоединить кнопку к LinearLayout в активности или фрагменте.
Мы указали, что хотим надуть кнопку из его собственной разметки и присоединить полученную кнопку к LinearLayout (второй параметр). Это решение эквивалентно вызову метода с двумя параметрами.
Как вариант, можно не присоединять при надувании, а добавить программно через addView().
Другой случай использования true встречается при создании собственного компонента, когда применяется .
У файла разметки нет корневого ViewGroup и мы указываем свой собственный компонент на основе LinearLayout в качестве корневого. Если бы мы использовали в своей разметке вместо merge любой компонент, например, FrameLayout, то тогда был бы другой случай.
Теперь вы знаете, в каких ситуациях нужно использовать разные методы inflate(). Это как с котом — надутого кота можно обратить в null, когда вы не нуждаетесь в двойниках.
Источник
Fragment (Фрагменты)
Фрагменты немного пугают новичков. Постараюсь объяснить как можно проще, чтобы отдельные фрагменты пазла сложились в единую картину.
Зачем?
Создатели операционной системы Android оказались недальновидными разработчиками. Не посоветовавшись с котами, они разработали систему под маленькие экраны телефонов. Но котам было неудобно пользоваться такими экранами, и тогда придумали планшеты.
Старые программы прекрасно на них запускались, но обнаружилось несколько недостатков. На больших экранах интерфейс выглядел не слишком элегантно, появились большие пустые пространства. И тогда возникла идея объединить два отдельных экрана из смартфона в один экран на планшете. Это самый классический пример применения фрагмента. По сути, это костыль. Возможно, если бы сразу подумали головой, то придумали бы более элегантное решение. Но теперь поздно пить «Боржоми», будем использовать предложенную концепцию.
Фрагменты были представлены в API 11 (Android 3.0), но в целях совместимости была написана специальная библиотека Android Support library для старых устройств. Долгое время существовало два класса Fragment: для новых устройств и для старых устройств. Названия методов и классов были очень похожи, и разработчики часто путались, смешивая в одном проекте два разных несовместимых класса. Спустя некоторое время решили отказаться от странного разделения, класс для новых устройств признали устаревшим, а класс из библиотеки поддержки старых устройств сменил своё полное имя и вошёл в состав AndroidX.
При желании можно было продолжить писать приложения в старом стиле, отслеживая размеры экрана. Но такой код получится слишком сложным. Пришлось бы писать один код для переключения от одной активности к другой при использовании смартфона и другой код, когда взаимодействие между объектами происходит на одном экране в планшете. Чтобы устранить это противоречие, были придуманы фрагменты. Хотя там тоже придётся писать много кода.
Несколько слов о том, как проще воспринимать фрагмент. Считайте, что фрагмент — это тот же компонент как Button, TextView или LinearLayout с дополнительными возможностями. Фрагмент, как и кнопку, нужно поместить на экран активности. Но фрагмент является модульным компонентом и один и тот же фрагмент можно встроить в две разные активности. С кнопкой такой номер не пройдёт. Для каждой активности вы должны создать свою отдельную кнопку, даже если их нельзя будет отличить друг от друга.
Фрагмент также немного похож на активность. Но фрагменты — это не замена активности, они не существуют сами по себе, а только в составе активностей. Поэтому в манифесте прописывать их не нужно. Но в отличие от стандартной кнопки, для каждого фрагмента вам придётся создавать отдельный класс, как для активности.
В составе активности есть специальный менеджер фрагментов, который может контролировать все классы фрагментов и управлять ими. О нём позже.
Фрагменты являются строительным материалом для приложения. Вы можете в нужное время добавить новый фрагмент, удалить ненужный фрагмент или заменить один фрагмент на другой. Точно так же мы собираем пазл — подносим фрагмент кота в общую картину, иногда ошибаемся и тогда заменяем кусочек пазла на другой и т.д.
Фрагмент может иметь свою разметку, а может обойтись без неё. Также у фрагмента есть свой жизненный цикл, во многом совпадающий с жизненным циклом активности. Пожалуй, это единственное сходство с активностью.
Имеются специальные виды фрагментов, заточенные под определённые задачи — ListFragment, DialogFragment и другие, которые изучим в других уроках.
Есть два варианта использования фрагментов в приложении (при желании можно использовать сразу оба варианта). Первый вариант заключается в том, что вы в разметке сразу указываете фрагмент с помощью тега fragment, так же как и с другими компонентами.
Второй вариант использует динамическое подключение фрагмента. Принцип следующий — в разметку помещается макет из группы ViewGroup, который становится контейнером для фрагмента. Обычно, для этой цели используют FrameLayout, но это не обязательное условие. И в нужный момент фрагмент замещает контейнер и становится частью разметки.
Поначалу фрагменты кажутся неудобными, так как количество кода увеличивается. Но если с ними работать постоянно, то станет понятнее их принцип.
Реакция разработчиков на появление фрагментов противоречива. Кто-то активно использует их в своих проектах, а кто-то их не переносит и использует альтернативные варианты. Похоже, в стане Гугла также идёт борьба между двумя группами программистов. Фрагменты постоянно развиваются, меняются и дорабатываются.
Первое знакомство
Для первого знакомства создадим стандартный проект на базе Empty Activity и вручную добавим фрагмент. Итак, после создания проекта выбираем из контекстного меню пакета New | Fragment | Fragment (Blank). В диалоговом окне мастера назначаем имя для фрагмента.
На этом этапе создание нового фрагмента напоминает создание новой активности. Мы создаём новый класс и автоматически генерируется макет для него fragment_cat.xml. Единственное отличие, в манифест ничего не добавляется.
Откроем макет фрагмента и немного отредактируем содержимое, добавив центрирование по центру. Также можно было добавить ImageView с изображением кота. Но не будем пока ничего усложнять.
В res/values/strings.xml содержится ресурс hello_blank_fragment. Изменим текст на что-то понятное:
Приготовления закончены. Осталось добавить фрагмент в активность. Открываем activity_main.xml и добавляем новый элемент.
В элементе fragment в атрибуте android:name указываем полное имя класса фрагмента.
Можно запустить проект и увидеть фрагмент внутри активности.
Жизненный цикл фрагмента
У фрагмента есть жизненный цикл, как у активности. Но число методов цикла гораздо больше. Откроем теперь файла класса фрагмента CatFragment и добавим новый код (выделено жирным).
Чтобы увидеть связь между жизненными циклами фрагмента и активности, добавим логи и в активность.
Снова запускаем проект и смотрим логи.
У фрагментов есть жизненный цикл, который во многом совпадает с жизненным циклом активности, внутри которой они находятся.
Два отдельных фрагмента
Мы научились размещать один фрагмент на экране активности. Ничто не мешает разместить на экране несколько фрагментов, которые являются контейнерами для компонентов. Давайте совместим на одном экране два старых примера — «Hello Kitty» и «Счётчик ворон/котов».
Повторим шаги создания нового фрагмента через готовый шаблон Fragment (Blank) и создадим два новых фрагмента KittyFragment и CounterFragment.
Разметку для KittyFragment копируем из старого примера.
Код также копируем с небольшим отличием. Если в активности мы размещали основной код в методе onCreate(), то для фрагмента используем onViewCreated(). Код шаблона для экономии я опущу.
Для счётчика котов (ворон больше считать не будем) напишем продвинутую версию, которая позволить не только увеличивать показания счётчика, но и уменьшать. В конце концов коты имеют привычку гулять сами по себе и пропадают из поля нашего зрения.
Разметка для фрагмента counter_fragment.xml.
Код для класса CounterFragment. Здесь также мы добавляем весь код в onViewCreated().
Осталось разместить два фрагмента в activity_main.xml.
Запускаем проект и видим два приложения в одном. В верхней части мы здороваемся с котёнком, а в нижней считаем котов. Обратите внимание, что весь код сосредоточился в классах фрагментов, а в MainActivity нам вообще ничего не пришлось писать. Фрагменты работают независимо и мы можем писать код в разных модулях.
Источник