Диалог, который ведёт себя правильно
«А как вы покажете диалог подтверждения?» — казалось бы, простой вопрос. Но на собеседованиях он моментально делит кандидатов на тех, кто пишет «диалоги через AlertDialog и молится», и тех, кто знает про DialogFragment.
Диалоговые окна — это неотъемлемая часть любого Android-приложения: подтверждение действия, выбор из списка, ввод текста. Вопрос про них — любимый у интервьюеров, потому что за ним стоит сразу несколько проверок: понимаете ли вы жизненный цикл, знаете ли разницу между системным Dialog и DialogFragment, умеете ли переживать поворот экрана без потери состояния.
Проблема в том, что «простой» путь — создать AlertDialog и показать его напрямую — в реальном приложении ломается. Диалог не переживает поворот экрана, не сохраняет состояние, не умеет корректно обрабатывать нажатие «Назад». Поэтому в Android есть DialogFragment — и в этой статье разберём, что это такое, как он работает и что говорить на собеседовании.
Что такое DialogFragment
DialogFragment — это фрагмент, специально созданный для показа диалоговых окон. Он наследуется от Fragment и получает все его возможности: собственный жизненный цикл, управление состоянием, восстановление после изменения конфигурации.
Официальная документация говорит прямо: DialogFragment предоставляет все необходимые элементы управления для создания диалогового окна и управления его внешним видом — вместо прямых вызовов методов объекта Dialog.
Зачем это нужно? Три главные причины:
- Корректная работа с жизненным циклом.
DialogFragmentправильно обрабатывает нажатие кнопки «Назад», поворот экрана и уход приложения в фон. ОбычныйAlertDialog, показанный «в лоб», этим не управляет — при ротации он просто пропадёт или упадёт. - Сохранение состояния. Как и любой фрагмент,
DialogFragmentвосстанавливается после изменения конфигурации, а его состояние можно сохранить черезonSaveInstanceState. - Переиспользование UI. Интерфейс диалога можно встроить в экран как обычный фрагмент — например, на планшете показывать его как часть макета, а на телефоне — как диалог поверх экрана.
Главная мысль
DialogFragment — это не «ещё один способ показать диалог», а полноценный фрагмент с жизненным циклом, который умеет быть диалогом. Именно поэтому он управляется через FragmentManager — так же, как обычные экраны-фрагменты.
Кстати, важный нюанс для собеседования: DialogFragment живёт в FragmentManager. Его показывают через транзакцию фрагментов, а не через dialog.show(). Поэтому и back stack, и восстановление после ротации работают так же, как у обычных фрагментов.
Создание: onCreateDialog и show
Создать DialogFragment можно двумя способами: переопределить onCreateDialog() и вернуть готовый диалог (например, AlertDialog) или переопределить onCreateView() и построить собственный layout.
Классический пример из документации Android — диалог через AlertDialog.Builder:
StartGameDialogFragment.kt
import android.app.Dialog
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
class StartGameDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
val builder = AlertDialog.Builder(it)
builder.setMessage("Начать игру?")
.setPositiveButton("Начать") { _, _ ->
// Пользователь подтвердил действие
}
.setNegativeButton("Отмена") { _, _ ->
// Пользователь отменил
}
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
}Показывают диалог через show() с указанием FragmentManager и тега:
MainActivity.kt
class MainActivity : AppCompatActivity() {
fun showStartDialog() {
StartGameDialogFragment().show(supportFragmentManager, "start_game")
}
}Тег нужен для того, чтобы найти уже показанный диалог через findFragmentByTag() — например, чтобы не показывать второй такой же.
Для диалога со своим дизайном переопределяют onCreateView() и возвращают инфлейтнутый layout — как в обычном фрагменте. Выбор зависит от задачи:
onCreateDialog()— когда достаточно стандартногоAlertDialogс сообщением и кнопками;onCreateView()— когда нужен полностью кастомный интерфейс.
Кандидаты говорят «onCreateView и onCreateDialog можно вызвать оба» — и это ошибка. Обычно используют что-то одно. Если переопределён onCreateDialog(), по умолчанию onCreateView() создаёт view, взятое из Dialog; переопределять оба без необходимости — запутывать код.
Жизненный цикл и состояние
Здесь кандидатов ждёт самый коварный вопрос: «Что будет с диалогом при повороте экрана?».
Если вы показали обычный AlertDialog через dialog.show() — при ротации Activity пересоздаётся, а диалог, созданный в onCreate, теряется. Диалог из старого Activity может даже привести к утечке: он держит ссылку на уничтоженную Activity. Это классическая проблема, из-за которой Android-сообщество и перешло на DialogFragment.
DialogFragment при повороте ведёт себя как любой фрагмент: система сохраняет его состояние, пересоздаёт вместе с Activity, и диалог снова появляется на экране. Данные, которые нужно пережить ротацию, сохраняются в onSaveInstanceState — так же, как в обычном фрагменте.
ConfirmDialogFragment.kt
class ConfirmDialogFragment : DialogFragment() {
companion object {
const val ARG_MESSAGE = "message"
fun newInstance(message: String): ConfirmDialogFragment {
return ConfirmDialogFragment().apply {
arguments = Bundle().apply { putString(ARG_MESSAGE, message) }
}
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val message = requireArguments().getString(ARG_MESSAGE)
return AlertDialog.Builder(requireContext())
.setMessage(message)
.setPositiveButton("OK") { _, _ -> dismiss() }
.create()
}
}Обратите внимание на два момента в этом коде:
argumentsвместо передачи параметров через конструктор. Фрагмент может быть пересоздан системой — конструктор с параметрами при этом вызовется снова без аргументов. Данные должны жить вargumentsиsavedInstanceState. Это обязательное правило для любого фрагмента.requireContext()иrequireArguments()— методы, которые бросают исключение, если фрагмент не привязан к контексту или аргументы не заданы. Намного безопаснее, чем молча работать сactivity.
Что ответить про ротацию
Обычный AlertDialog создаётся в коде Activity и при пересоздании Activity пропадает. DialogFragment переживает ротацию, потому что он — фрагмент: система сохраняет его состояние и пересоздаёт диалог автоматически. Параметры диалога передаются через arguments, а не через конструктор.
DialogFragment, Dialog и AlertDialog: не путать
На собеседованиях эти три понятия смешивают чаще всего. Разложим по полочкам:
Dialog— базовый класс системного окна. Умеет показываться поверх экрана, но сам по себе не управляется жизненным циклом и не сохраняет состояние.AlertDialog— наследникDialogс готовой структурой: заголовок, контент, до трёх кнопок действий. Это «диалог из коробки» для стандартных сценариев.DialogFragment— фрагмент-контейнер, который показывает внутри себя любой диалог (чаще всегоAlertDialog). Добавляет жизненный цикл фрагмента, сохранение состояния и управление черезFragmentManager.
Простая аналогия для ответа: AlertDialog — это сам «домик» (что показать), а DialogFragment — «фундамент» (как показать, чтобы пережить поворот экрана, сохранить состояние и корректно закрыться).
Ещё один момент, который любят проверять: как закрыть диалог. У DialogFragment есть метод dismiss(), который убирает диалог и удаляет фрагмент из менеджера. А нажатие системной кнопки «Назад» закрывает диалог и тоже обрабатывается корректно — в отличие от «сырого» Dialog, где за обработку «Назад» вы отвечаете сами.
«А что, если показать диалог после того, как Activity ушла в фон или уничтожена?» — FragmentManager это отсекает: commit() после сохранения состояния Activity не выполнится. Поэтому безопасный паттерн — проверять isAdded или показывать диалог через lifecycleScope/viewLifecycleOwner, а не «просто вызывать show() из любого места».
Ошибки, которые я вижу на собеседованиях
Я провожу технические собеседования и вот какие промахи про диалоги встречаются чаще всего.
- «Диалог — это
AlertDialog.show()и всё». Обычный диалог не переживёт поворот экрана и не сохранит состояние. На вопрос «а если пользователь повернёт телефон?» кандидат теряется. - Параметры в конструкторе.
ConfirmDialogFragment("text")ломается при пересоздании системой: фрагмент восстановится через пустой конструктор и аргументы не получит. Правильно —newInstance()сarguments. - «
DialogFragmentне нужен, естьDialog». Это спорная позиция, но в современных приложениях она не выдерживает критики: потеря состояния при ротации — это баг, который пользователь увидит сразу. - Путают
onCreateDialogиonCreateView. Обычно выбирают один способ: стандартныйAlertDialog— черезonCreateDialog, кастомный layout — черезonCreateView. - Забывают, что
DialogFragment— это фрагмент. Значит, работают все правила фрагментов:arguments,FragmentManager, back stack,onSaveInstanceState. Кто это проговаривает — сразу выделяется на фоне остальных.
Если вы собираетесь на собеседование в компанию уровня Яндекс или Авито, вопрос про диалоги почти гарантированно всплывёт — обычно в связке с жизненным циклом и сохранением состояния. Это базовая тема, но именно на ней видно, писал ли человек реальные приложения или только читал про компоненты.
Хотите проверить готовность к собеседованию?
Бесплатная диагностика: разберу ваши пробелы и составлю план роста за 30–40 минут. Без обязательств — если менторство вам не подойдёт, честно скажу.
Итоги
Три тезиса, которые нужно держать в голове перед ответом про DialogFragment.
DialogFragment — это фрагмент, который умеет быть диалогом. Он наследует все возможности Fragment: жизненный цикл, управление через FragmentManager, сохранение состояния. Поэтому он корректно переживает поворот экрана и нажатие «Назад» — то, с чем «сырой» AlertDialog не справляется.
Создание — через onCreateDialog() или onCreateView(), показ — через show(manager, tag). Стандартный диалог с кнопками — AlertDialog.Builder в onCreateDialog(). Кастомный интерфейс — layout в onCreateView(). Параметры передаются через arguments, а не через конструктор.
Dialog и AlertDialog — это «что показать», DialogFragment — «как показать правильно». Если интервьюер копает в сторону состояния при ротации — отвечайте через механику фрагментов, а не через методы диалога.
Формула ответа
DialogFragment = Fragment + Dialog. Как фрагмент — живёт в FragmentManager, переживает ротацию, сохраняет состояние через arguments и onSaveInstanceState. Как диалог — показывает AlertDialog или кастомный view и закрывается через dismiss(). Проговорили эту связку — вопрос закрыт.
Если хотите потренироваться отвечать на такие вопросы перед собеседованием — напишите в Telegram, разберём вашу ситуацию и составим план подготовки.