
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?