Как настроить обратную навигацию

Пользователи перемещаются назад по экранам с помощью обратной навигации. На большинстве устройств Android есть кнопка "Назад" (физическая, программная или на основе жестов). Обычно добавлять кнопку "Назад" в приложение не нужно. Однако на устройствах с Android Automotive OS (AAOS) в режиме совместимости используется системная кнопка "Назад". Навигация будет добавлена автоматически, поэтому вам не нужно делать это самостоятельно. Подробнее о режиме совместимости с AAOS…

Android поддерживает обратный стек пунктов назначения, когда пользователь перемещается по вашему приложению. Обычно это позволяет Android правильно переходить к предыдущим пунктам назначения при нажатии кнопки "Назад". Однако в некоторых случаях в приложении может потребоваться реализовать собственное поведение кнопки "Назад", чтобы обеспечить наилучшее взаимодействие с пользователем. Например, при использовании WebView вы можете переопределить поведение кнопки "Назад" по умолчанию, чтобы пользователь мог перемещаться назад по истории браузера, а не по предыдущим экранам приложения.

Как реализовать собственную обратную навигацию в Compose

В Jetpack Compose можно реализовать собственную навигацию назад с помощью компонента BackHandler.

При использовании Navigation Compose для перехода к предыдущему экрану в обратном стеке обычно применяется NavController.navigateUp() или NavController.popBackStack(). Однако BackHandler полезен в случаях, когда вы хотите реализовать пользовательское поведение, когда пользователь нажимает системную кнопку «Назад» или использует жест «Назад». Например, если в приложении есть WebView, вы можете разрешить пользователям переходить назад по истории браузера, нажимая системную кнопку "Назад".

Как пользоваться отчетом

Ниже приведен пример того, как перехватить событие "Назад", чтобы управлять пользовательским состоянием (например, свернуть панель):

@Composable
fun MyScreen() {
    var isExpanded by remember { mutableStateOf(false) }

    // Intercept the back button or gesture only when isExpanded is true
    BackHandler(enabled = isExpanded) {
        // Collapsing the content disables this handler, so it does not
        // receive the next back event.
        isExpanded = false
    }

    if (isExpanded) {
        Text("Expanded content. Go back to collapse it.")
    } else {
        Button(onClick = { isExpanded = true }) {
            Text("Expand")
        }
    }
}

Перехват компонуемого дерева

Если у вас несколько включенных компонентов BackHandler на разных уровнях дерева компонентов, событие "Назад" перехватывает только самый внутренний из них.

Как реализовать собственную обратную навигацию с помощью представлений

ComponentActivity – базовый класс для FragmentActivity и AppCompatActivity. Он позволяет управлять поведением кнопки "Назад" с помощью OnBackPressedDispatcher, который можно получить, вызвав getOnBackPressedDispatcher().

OnBackPressedDispatcher управляет тем, как события кнопки "Назад" отправляются одному или нескольким объектам OnBackPressedCallback. Конструктор для OnBackPressedCallback принимает логическое значение для исходного состояния. Только когда обратный вызов включен, например когда isEnabled() возвращает true, диспетчер вызовет handleOnBackPressed() обратного вызова для обработки события кнопки "Назад". Чтобы изменить состояние, вызовите метод setEnabled().

Обратные вызовы добавляются с помощью методов addCallback. Используйте метод addCallback(), который принимает LifecycleOwner. Таким образом, OnBackPressedCallback добавляется, только если LifecycleOwner – Lifecycle.State.STARTED. Действие также удаляет зарегистрированные обратные вызовы, когда связанный с ними объект LifecycleOwner уничтожается. Это предотвращает утечки памяти и позволяет использовать обратные вызовы во фрагментах и других владельцах жизненного цикла, которые существуют меньше, чем действие.

Пример реализации обратного вызова:

class MyFragment : Fragment() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // This callback will only be called when MyFragment is at least Started.
        val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
            // Handle the back button event
        }

        // The callback can be enabled or disabled here or in the lambda
    }
    ...
}

Вы можете указать несколько функций обратного вызова, используя addCallback(). При этом обратные вызовы вызываются в обратном порядке, в котором они добавляются: обратный вызов, добавленный последним, первым получает возможность обработать событие кнопки "Назад". Например, если вы добавили три обратных вызова с названиями one, two и three, они будут вызываться в следующем порядке: three, two и one.

Обратные вызовы следуют шаблону Цепочка обязанностей. Каждый обратный вызов в цепочке выполняется, только если предыдущий не был включен. Это означает, что в предыдущем примере обратный вызов two будет выполнен, только если обратный вызов three не включен. Обратный вызов one будет выполнен, только если не включен обратный вызов two. Это правило действует и для всех последующих обратных вызовов.

Обратите внимание, что при добавлении с помощью addCallback() обратный вызов не добавляется в цепочку ответственности, пока LifecycleOwner не перейдет в состояние Lifecycle.State.STARTED.

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

Чтобы полностью удалить обратный вызов, вызовите метод remove(). Обычно это не требуется, поскольку обратные вызовы автоматически удаляются, когда связанный с ними LifecycleOwner уничтожается.