Android Jetpack Navigation с ViewPager и TabLayout

Для нового приложения я использую навигационную библиотеку Jetpack для правильной обратной навигации. Первый уровень навигации - это навигационный ящик, который отлично работает с навигацией JetPack, как описано в документации. Но есть еще один уровень навигации, реализованный с помощью ViewPager и TabLayout. Фрагменты, переключаемые TabLayout, содержат дополнительную линейную навигационную иерархию. Однако, похоже, поддержка ViewPager/TabLayout в Jetpack Navigation не поддерживается. Адаптер FragmentPagerAdapter должен быть реализован, а управляемый задний стек заканчивается при переключении вкладок. Существует разрыв между навигацией верхнего уровня и навигацией внутри каждой вкладки. Есть ли способ сделать эту работу с помощью Jetpack Navigation?

Ответ 1

Что сработало для меня до сих пор:

В navigation_graph.xml

  • сделайте ваш ViewPagerFragment корнем вложенного графа
  • подключите входную и выходную навигацию к вложенному графику

во вложенном графике:

  • добавить дочерние фрагменты ViewPager на вложенный граф

Мне не нужно было менять ViewPager, и для дочерних фрагментов были созданы направления, так что навигация возможна оттуда.

Ответ 2

Да, но вам придется реализовать свой собственный пункт назначения, реализуя класс Navigator и переопределяя как минимум методы popBackStack() и navigate().

В вашей navigate вам нужно будет вызвать ViewPager.setCurrentTab() и добавить его в свой задний стек. Что-то вроде:

lateinit var viewPager: ViewPager? = null // you have to provide this in the constructor

private val backstack: Deque<Pair<Int, View>> = ArrayDeque

override fun navigate(destination: Destination, args: Bundle?,
                      navOptions: NavOptions?, navigatorExtras: Extras?
): NavDestination? {

    viewPager.currentItem = destination.id
    backstack.remove(destination.id) // remove so the stack has never two of the same
    backstack.addLast(destination.id)

    return destination
}

В вашем popBackStack вам придется вернуть последний выбранный элемент. Что-то вроде:

override fun popBackStack(): Boolean {
    if(backstack.size() <= 1) return false

    viewPager.currentItem = backstack.peekLast()
    backstack.removeLast()

    return true
}

Вы можете найти краткое объяснение на Android документах и этот пример пользовательского навигатора для FragmentDialog.

После реализации ViewPagerNavigator вам нужно будет добавить его в NavController и настроить прослушиватели выбора представлений вкладок для вызова NavController.navigate().

Я надеюсь, что кто-то реализует библиотеку для всех этих общих шаблонов (ViewPager, ViewGroup, FragmentDialog), если кто-нибудь найдет ее, поместите ее в комментарии.