Fragment
Fragment
Представляет часть пользовательского интерфейса или логики, который может быть встроен в Activity
. Фрагменты помогают создавать динамичные и многоразовые интерфейсы.
class MyFragment: Fragment(R.layout.fragment_my) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}
onAttach
Вызывается, когда фрагмент прикрепляется к Activity
.
override fun onAttach(context: Context) {
super.onAttach(context)
println("Fragment attached to Activity")
}
onCreate
Инициализация фрагмента, настройка данных, которые сохраняются при пересоздании.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
println("Fragment created")
}
onCreateView
Создание иерархии View
.
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
println("Creating fragment view")
return inflater.inflate(R.layout.fragment_layout, container, false)
}
onViewCreated
Вызывается сразу после onCreateView
. Инициализация компонентов View
.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
println("View created")
}
onAcyivityCreated
Вызывается, когда Activity
завершила onCreate
.
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
println("Activity created for fragment")
}
onViewStateRestored
Восстановление состояния View
фрагмента.
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
println("View state restored")
}
onStart
Фрагмент становится видимым для пользователя.
override fun onStart() {
super.onStart()
println("Fragment started")
}
onResume
Фрагмент становится активным и начинает взаимодействовать с пользователем.
override fun onResume() {
super.onResume()
println("Fragment resumed")
}
onPause
Вызывается перед тем, как фрагмент перестанет быть активным.
override fun onPause() {
super.onPause()
println("Fragment paused")
}
onStop
Фрагмент больше не видим.
override fun onStop() {
super.onStop()
println("Fragment stopped")
}
onDestroyView
Освобождение ресурсов, связанных с View
.
override fun onDestroyView() {
super.onDestroyView()
println("Fragment view destroyed")
}
onDestroy
Фрагмент уничтожается. Очищение ресурсов.
override fun onDestroy() {
super.onDestroy()
println("Fragment destroyed")
}
onDetach
Фрагмент отсоединяется от Activity
.
override fun onDetach() {
super.onDetach()
println("Fragment detached from Activity")
}
setRetainInstance
Позволяет сохранить экземпляр фрагмента при пересоздании Activity
, например, при повороте экрана. Это удобно для хранения долгоживущих объектов или фоновых задач, которые не должны пересоздаваться.
class MyFragment: Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
}
}
FragmentManager
Управляет фрагментами в Android. С его помощью можно добавлять, удалять и заменять фрагменты в активности или другом фрагменте через childFragmentManager
. Это позволяет создавать гибкие интерфейсы, которые меняются во время работы приложения.
supportFragmentManager
Поле класса AppCompatActivity
, которое предоставляет доступ к экземпляру FragmentManager
, совместимому Support Library. Оно используется для управления фрагментами в приложениях, которые используют компоненты из библиотеки поддержки, что позволяет обеспечивать совместимость с более старыми версиями Android.
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, FirstFragment())
.commit()
}
}
}
parentFragmentManager
Поле класса Fragment
, которое предоставляет доступ к FragmentManager
родительского фрагмента. Оно используется, когда есть вложенные фрагменты (child fragments) и необходимо взаимодействовать с родительским FragmentManager
, а не с childFragmentManager
.
class ChildFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
parentFragmentManager
.beginTransaction()
.replace(R.id.fragment_container, NewParentFragment())
.addToBackStack(null)
.commit()
}
}
childFragmentManager
Поле класса Fragment
, которое предоставляет доступ к FragmentManager
для управления дочерними фрагментами. Оно используется, когда есть фрагмент, который содержит другие фрагменты внутри себя (вложенные фрагменты), и нужно управлять их жизненным циклом и взаимодействием.
class ParentFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
childFragmentManager
.beginTransaction()
.add(R.id.child_fragment_container, ChildFragment())
.commit()
}
}
add
Используется для добавления фрагмента в контейнер. При добавлении нового фрагмента через add
, предыдущий фрагмент остается активным, его жизненный цикл не меняется, он продолжает работать в RESUMED
состоянии.
supportFragmentManager
.beginTransaction()
.add(R.id.fragment_container, MyFragment(), "myFragmentTag")
.commit()
replace
Удаляет текущий фрагмент в контейнере и добавляет новый. Это замещает один фрагмент другим, вызывая полный жизненный цикл нового фрагмента от onCreate()
до onResume()
и уничтожение предыдущего фрагмента от onPause()
до onDestroy()
.
supportFragmentManager
.beginTransaction()
.replace(R.id.fragment_container, FragmentB())
.commit()
remove
Удаляет фрагмент из FragmentManager
, что означает, что фрагмент больше не отображается на экране. Однако состояние фрагмента сохраняется в FragmentManager
, и его можно восстановить, если это необходимо.
supportFragmentManager
.beginTransaction()
.remove(fragment)
.commit()
addToBackStack
Добавляет текущую транзакцию фрагментов в стек возврата, что позволяет пользователю вернуться к предыдущему состоянию фрагментов при нажатии кнопки «Назад».
supportFragmentManager
.beginTransaction()
.replace(R.id.fragment_container, secondFragment)
.addToBackStack(null)
.commit()
popBackStack
Удаляет верхний фрагмент из стека возврата. Это приводит к тому, что фрагмент, который был перед ним, становится видимым, а текущий фрагмент удаляется.
// Удаление последнего фрагмента из стека возврата
supportFragmentManager.popBackStack()
// Удаление всех фрагментов до фрагмента с именем "myFragmentName"
supportFragmentManager.popBackStack("myFragmentName", FragmentManager.POP_BACK_STACK_INCLUSIVE)
popBackStackImmediate
Работает аналогично методу popBackStack
, но выполняет операцию немедленно, а не асинхронно.
// Немедленное удаление последнего фрагмента из стека возврата
supportFragmentManager.popBackStackImmediate()
// Немедленное удаление всех фрагментов до фрагмента с именем "myFragmentName"
supportFragmentManager.popBackStackImmediate("myFragmentName", FragmentManager.POP_BACK_STACK_INCLUSIVE)
executePendingTransactions
Заставляет FragmentManager
немедленно выполнить все ожидающие транзакции. Это отличается от методов commit
или commitNow
, которые фиксируют транзакции асинхронно или немедленно, но не обязательно выполняют их сразу.
supportFragmentManager.executePendingTransactions()
commit
Фиксирует изменения в FragmentTransaction
, сделанные до вызова этого метода. Это включает в себя добавление, удаление, замену или изменение фрагментов. Выполняет транзакцию асинхронно. Это значит, что изменения могут быть применены позже, в следующем цикле обновления UI. Если вызвать executePendingTransactions
после метода commit
, то транзакция станет синхронной.
supportFragmentManager
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit()
commitNow
Выполняет транзакцию синхронно, немедленно фиксируя изменения. Используйте этот метод, если нужно убедиться, что транзакция завершена до выполнения следующего кода.
supportFragmentManager
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commitNow()
commitAllowingStateLoss
Фиксирует транзакцию, игнорируя потенциальные потери состояния активити и фрагмента. Это может быть полезно, если состояние не критично и транзакция должна быть выполнена независимо от возможной потери состояния. Использование commitAllowingStateLoss
приводит к ситуациям такого вида: Пользователь возвращается на Activity
, которая была уничтожена системой. Пользователь ожидает увидеть UI, который отображался перед тем как он покинул активити, но транзакция с добавлением или удалением фрагмента не сохранилась и пользователь видит пустой экран или активити в неверном состоянии. Хорошей практикой считается использование commit
, а не commitAllowingStateLoss
. Если вы получаете репорты о state loss крэшах, пробуйте решить корень проблемы. Для этого не вызывайте commit
в onPause
или следующих после него методах.
supportFragmentManager
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commitAllowingStateLoss()
dismiss
Используется для закрытия DialogFragment
. Закрывает диалог с анимацией и планирует удаление фрагмента в следующей доступной транзакции, что позволяет избежать потенциальных проблем с состоянием фрагмента.
class MyDialogFragment: DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return AlertDialog.Builder(requireContext())
.setTitle("Title")
.setMessage("Message")
.setPositiveButton("OK") { _, _ ->
// Закрываем диалог с анимацией
dismiss()
}
.setNegativeButton("Cancel") { _, _ -> }
.create()
}
}
dismissNow
Используется для немедленного удаления фрагмента. В отличие от dismiss
, который может быть отложен до завершения текущего цикла событий, dismissNow
выполнит действие сразу же.
class MyDialogFragment: DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return AlertDialog.Builder(requireContext())
.setTitle("Title")
.setMessage("Message")
.setPositiveButton("OK") { _, _ ->
// Закрываем диалог немедленно
dismissNow()
}
.setNegativeButton("Cancel") { _, _ -> }
.create()
}
}
dismissAllowingStateLoss
Используется для удаления фрагмента, игнорируя потенциальные потери состояния. Он похож на метод dismiss
, но позволяет удалять фрагмент даже если это может привести к потере состояния активити или фрагмента.
class MyDialogFragment: DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return AlertDialog.Builder(requireContext())
.setTitle("Title")
.setMessage("Message")
.setPositiveButton("OK") { _, _ -> }
.setNegativeButton("Cancel") { _, _ ->
// Закрываем диалог, позволяя потерю состояния
dismissAllowingStateLoss()
}
.create()
}
}
POP_BACK_STACK_INCLUSIVE
Удаляет все фрагменты из стека возврата до указанного фрагмента и включает его в удаление. Используется, когда необходимо удалить фрагмент, соответствующий имени или ID, а также все фрагменты, добавленные после него.
// Добавляем фрагменты в стек возврата
supportFragmentManager.beginTransaction()
.replace(R.id.container, FirstFragment())
.addToBackStack("firstFragment")
.commit()
supportFragmentManager.beginTransaction()
.replace(R.id.container, SecondFragment())
.addToBackStack("secondFragment")
.commit()
// Вернуться к FirstFragment и удалить SecondFragment
supportFragmentManager.popBackStack("firstFragment", FragmentManager.POP_BACK_STACK_INCLUSIVE)
POP_BACK_STACK_EXCLUSIVE
Удаляет все фрагменты из стека возврата до указанного фрагмента, но не включает его в удаление. Используется, когда нужно оставить указанный фрагмент на вершине стека.
// Добавляем фрагменты в стек возврата
supportFragmentManager.beginTransaction()
.replace(R.id.container, FirstFragment())
.addToBackStack("firstFragment")
.commit()
supportFragmentManager.beginTransaction()
.replace(R.id.container, SecondFragment())
.addToBackStack("secondFragment")
.commit()
// Вернуться к FirstFragment, оставляя его на вершине стека
supportFragmentManager.popBackStack("firstFragment", FragmentManager.POP_BACK_STACK_EXCLUSIVE)
findFragmentByTag
Ищет фрагмент по заданному тегу. Используется, если фрагмент добавлен в FragmentTransaction
с помощью метода add
или replace
и ему присвоен тег.
val fragment = supportFragmentManager.findFragmentByTag("MY_FRAGMENT")
if (fragment != null) {
// Фрагмент найден
}
findFragmentById
Ищет фрагмент, привязанный к указанному ресурсу View
по его ID. Используется, если фрагмент был добавлен через XML-разметку или динамически с помощью FragmentTransaction
и указан ID контейнера.
val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
if (fragment != null) {
// Фрагмент найден
}
FragmentFactory
Управляет созданием фрагментов, упрощая их кастомную инициализацию. Вместо пустого конструктора используется метод instantiate
, который можно переопределить для передачи параметров или внедрения зависимостей.
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val repository = MyRepository()
supportFragmentManager.fragmentFactory = MyFragmentFactory(repository)
}
}
class MyFragmentFactory(
private val repository: MyRepository
): FragmentFactory() {
override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
return when (className) {
MyFragment::class.java.name -> MyFragment(repository)
else -> super.instantiate(classLoader, className)
}
}
}
class MyFragment(
private val repository: MyRepository
): Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val data = repository.getData()
return inflater.inflate(R.layout.fragment_my, container, false)
}
}
• С FragmentFactory
удобно использовать DI-фреймворки.
class MyFragmentFactory @Inject constructor(
private val repository: MyRepository
): FragmentFactory() {
override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
return when (className) {
MyFragment::class.java.name -> MyFragment(repository)
else -> super.instantiate(classLoader, className)
}
}
}
Вопросы на собесе (34)
Lifecycle (13)
- Жизненный цикл фрагмента?
onAttach()
→onCreate()
→onCreateView()
→onActivityCreated()
→onStart()
→onResume()
→onPause()
→onStop()
→onDestroyView()
→onDestroy()
→onDetach()
- В каком методе жизненного цикла фрагмента становится доступен Context?
onAttach()
- Вызовется ли метод onDetach() при замене одного фрагмента другим?
Да. Когда фрагмент удаляется из
Activity
(включая замену), он проходит через все этапы жизненного цикла, включаяonDetach()
, что означает, что он отвязывается отActivity
.
- Зачем введено разделение на onDestroy(), onDestroyView() и onDetach()?
Разделение на
onDestroy()
,onDestroyView()
иonDetach()
позволяет точно управлять жизненным циклом и ресурсами фрагмента:•
onDestroyView()
вызывается при уничтожении пользовательского интерфейса, позволяя освободить ресурсы, связанные с представлением.•
onDestroy()
вызывается при полном уничтожении фрагмента для выполнения завершающих действий.•
onDetach()
вызывается при отвязывании фрагмента от активности, что важно для предотвращения утечек памяти.
- Почему у фрагментов есть метод onDestroyView(), а у Activity нет?
Потому что в жизненном цикле фрагментов
View
может быть уничтожено отдельно от самого фрагмента. Это происходит, когда фрагмент убирается с экрана, но его экземпляр продолжает существовать. УActivity
нет такого метода, потому что егоView
уничтожается вместе с самойActivity
при завершении, и нет необходимости разделять уничтожениеView
и объектаActivity
.
- Когда у фрагмента вызывается метод onCreate?
onCreate
вызывается, когда фрагмент создается, но еще не связался с пользовательским интерфейсом.
- Какие методы жизненного цикла вызовутся у старого фрагмента при replace?
onPause()
→onStop()
→onDestroyView()
→onDestroy()
→onDetach()
- В каком методе жизненного цикла фрагмента следует подписываться на Flow?
В
onViewCreated()
. В этом методеView
фрагмента уже создана и доступна для использования, что позволяет безопасно обновлять UI на основе получаемых данных.
- Какой lifecycle owner фрагмента следует использовать для подписки на Flow?
viewLifecycleOwner
. Это позволяет автоматически управлять подпиской в соответствии с жизненным циклом представления фрагмента, предотвращая утечки памяти и исключая вызовы на уже уничтоженных вьюхах.
- Какие методы жизненого цикла фрагмента существуют?
•
oninflate()
•
onResume()
•
onReload()
•
onStart()
- Какой метод жизненного цикла фрагмента вызывается, когда фрагмент больше не виден пользователю?
•
onDetach()
•
onPause()
•
onDestroyView()
•
onStop()
- В каком методе фрагмент становится доступен Activity?
•
onCreate()
•
onBind()
•
onAttach()
•
onStart()
- Какой метод жизненного цикла фрагмента вызывается для создания иерархии пользовательского интерфейса?
•
onCreateView()
•
onAttach()
•
onCreate()
•
onStart()
- Жизненный цикл фрагмента?
FragmentManager (14)
- Для чего нужен FragmentManager?
FragmentManager
управляет фрагментами, позволяя добавлять, удалять и заменять их, а также обрабатывать транзакции для изменения интерфейса. Он сохраняет состояние фрагментов при изменении конфигурации и управляет стеком возврата, обеспечивая взаимодействие между фрагментами и хост-активити.
- Как работают транзакции у фрагментов?
Транзакции фрагментов управляют их добавлением, заменой и удалением через
FragmentManager
.commitNow()
выполняет изменения сразу, аcommit()
— с задержкой; с помощьюaddToBackStack()
можно вернуться к предыдущему состоянию.
- Что будет, если мы попытаемся закоммитить фрагмент, когда приложение свернуто?
Это может вызвать исключение
IllegalStateException
, так как фрагменты не могут быть изменены, когдаActivity
не находится в состоянииRESUMED
. Для безопасного добавления или замены фрагментов лучше проверять состояние активности и использовать методы, такие какisStateSaved()
, чтобы избежать ошибок.
- Для чего нужен метод commitAllowingStateLoss?
Выполняет транзакцию фрагмента, даже если состояние
Activity
потеряно, что предотвращает исключение при коммите.
- Разница методов add и replace?
Метод
add()
добавляет новый фрагмент поверх существующего, оставляя предыдущий активным и в состоянииresumed
, тогда как методreplace()
заменяет текущий фрагмент, вызывая у него полный жизненный цикл (сonPause()
доonDetach()
) перед добавлением нового фрагмента.
- Чем отличается tag в методах add и addToBackStack?
tag
в методеadd
используется для уникальной идентификации фрагмента вFragmentManager
. Это позволяет позже получить доступ к фрагменту с помощьюfindFragmentByTag
.addToBackStack
создает запись в стек возврата, позволяя пользователю вернуться к предыдущему состоянию интерфейса. Здесьtag
не имеет значения, но его можно использовать для более удобной идентификации при работе со стеком.
- Как закрыть сразу n фрагментов в стеке?
Удалить все фрагменты до определенного можно методом
fragmentManager.popBackStack("targetFragmentTag", FragmentManager.POP_BACK_STACK_INCLUSIVE)
.
- Есть стек фрагментов A-B-C-D, как из D вернутьс в A?
Чтобы вернуться с D к A, вызови
fragmentManager.popBackStack("A", FragmentManager.POP_BACK_STACK_INCLUSIVE)
.
- Если в контейнер добавлено 5 фрагментов и выполняем replace, сколько фрагментов удалится?
Удалятся все 5 фрагментов, потому что
replace
сначала удаляет все существующие фрагменты в указанном контейнере, а затем добавляет новый фрагмент.
- Как работает FragmentManager.POP_BACK_STACK_INCLUSIVE?
Удаляет все фрагменты из стека возврата до указанного фрагмента и включает его в удаление. Используется, когда необходимо удалить фрагмент, соответствующий имени или ID, а также все фрагменты, добавленные после него.
- Какие из перечисленных методов для коммита у FragmentTansaction не позволяют добавлять фрагменты в backstack?
•
commitNow()
•
commit()
•
commitAllowingStateLoss()
- Какой из следующих классов используется для работы с фрагментами в Android?
•
FragmentController
•
FragmentManager
•
FragmentHandler
•
FragmentActivity
- Является ли метод FragmentManager.commit() синхронным?
• Да, он синхронный.
• Нет, он асинхронный.
• Такого метода нет.
- Какой метод следует использовать для проверки наличия фрагмента в FragmentManager по его тегу?
•
locateFragmentByTag
•
findFragmentByTag
•
searchFragmentByTag
•
getFragmentByTag
- Для чего нужен FragmentManager?
Другие (7)
- Для чего нужен Fragment?
Fragment
нужен для создания многоэкранных интерфейсов и управления компонентами пользовательского интерфейса внутриActivity
. Он позволяет переиспользовать интерфейсные элементы, управлять состоянием и взаимодействием, а также обеспечивает более гибкую архитектуру приложений, особенно на устройствах с большими экранами.
- Является ли Fragment основным компонентом Android?
Нет.
- Чем Fragment отличается от Activity?
Activity
— один из основных Android-компонентов с полным жизненным циклом, представляющий экран, аFragment
— это часть пользовательского интерфейса внутриActivity
, зависимая от его жизненного цикла и позволяющая переиспользовать интерфейсные элементы.
- Чем Fragment отличается от View?
Fragment
— это компонент с собственным жизненным циклом, который управляет несколькимиView
, тогда какView
— это базовый элемент интерфейса, зависящий от активности или фрагмента.
- Какие есть способы создать фрагмент?
• Использовать статический метод
newInstance()
для создания экземпляра и передачи данных черезBundle
(является безопасным и правильным подходом).• Через XML.
• Через
FragmentFactory
.
- Какие есть способы передать данные между фрагментами?
• Arguments
• Shared ViewModel
• Singleton
- Какой метод используется для закрытия диалога в Android?
•
dismiss()
•
cancelDialog
•
closeDialog
•
dismissDialog
- Для чего нужен Fragment?