我有一个带有BottomNavigationView
和ViewPager
的应用程序。
如何使用新的“导航架构组件”实现它?
最佳做法是什么?
非常感谢
答案 0 :(得分:9)
BottomNavigationView
与导航拱形组件的默认实现对我来说不可行。单击选项卡时,它会根据导航图从头开始。
我需要在屏幕底部拥有5个标签,并且每个标签都有一个单独的后盖。这意味着在标签之间切换时,您将始终返回到离开前的状态(如Instagram)。
我的方法如下:
ViewPager
和BottomNavigationView
放入activity_main.xml
OnNavigationItemSelectedListener
中将BottomNavigationView
设置为MainActivity.kt
NavHostFragment
。注意:每个图都可以互相影响。
重要的一点是,我们将工具栏不是置于活动中,而是置于容器片段中。然后,我们在工具栏本身上调用setupWithNavController()
,而未将其设置为supportActionBar
。这样,工具栏标题将自动更新,并且 Back / Up 按钮将被自动管理。
结果:
SafeArgs
和DeepLinking
正常工作。BottomNavigationManager
和ViewPager
拥有完全控制权(即,我们可以实现OnNavigationItemReselectedListener
并决定在弹出后退堆栈之前将当前选项卡中的列表滚动到顶部)。代码:
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.viewpager.widget.ViewPager
android:id="@+id/main_view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/main_bottom_navigation_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:menu="@menu/navigation" />
</LinearLayout>
MainActivity.kt
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private lateinit var viewPagerAdapter: ViewPagerAdapter
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_tab_1 -> {
main_view_pager.currentItem = 0
return@OnNavigationItemSelectedListener true
}
R.id.navigation_tab_2 -> {
main_view_pager.currentItem = 1
return@OnNavigationItemSelectedListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewPagerAdapter = ViewPagerAdapter(supportFragmentManager)
main_view_pager.adapter = viewPagerAdapter
main_bottom_navigation_view.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
}
}
ViewPagerAdapter.kt
class ViewPagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
return when (position) {
0 -> Tab1ContainerFragment()
else -> Tab2ContainerFragment()
}
}
override fun getCount(): Int {
return 2
}
}
fragment_tab_1_container.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Tab1ContainerFragment">
<androidx.appcompat.widget.Toolbar
android:id="@+id/tab_1_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark" />
<fragment
android:id="@+id/tab_1_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/navigation_graph_tab_1" />
</RelativeLayout>
Tab1ContainerFragment.kt
class Tab1ContainerFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_tab_1_container, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val toolbar = view.findViewById<Toolbar>(R.id.tab_1_toolbar)
val navHostFragment = childFragmentManager.findFragmentById(R.id.tab_1_nav_host_fragment) as NavHostFragment? ?: return
val navController = navHostFragment.navController
val appBarConfig = AppBarConfiguration(navController.graph)
toolbar.setupWithNavController(navController, appBarConfig)
}
}
我们可以根据需要创建任意数量的导航图:
但是我们需要为每个标签创建一个单独的图形:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/navigation_graph_tab_1"
app:startDestination="@id/tab1StartFragment">
<fragment
android:id="@+id/tab1StartFragment"
android:name="com.marat.android.bottomnavigationtutorial.Tab1StartFragment"
android:label="fragment_tab_1_start"
tools:layout="@layout/fragment_tab_1_start">
<action
android:id="@+id/action_tab_1_to_content"
app:destination="@id/navigation_graph_content" />
</fragment>
<include app:graph="@navigation/navigation_graph_content" />
</navigation>
此处的起始目标片段是您要在标签中的第一个屏幕上显示的任何片段。
答案 1 :(得分:2)
我创建了一个在Activity上具有工具栏的示例,您也可以创建具有自己的工具栏的ViewPager片段。它使用OnBackPressedCallback
进行向后导航,使用ViewModel
设置具有NavController
或嵌套片段的当前NavHostFragment
和childFragmentManager
,并使用viewLifeCycleOwner尊重生命周期并在其上禁用回调暂停并启用onResume。
导航和布局架构
MainActivity(Appbar + Toolbar + ViewPager2 + BottomNavigationView)
|
|- HomeNavHostFragment
| |- HF1 -> HF2 -> HF3
|
|- DashboardNavHostFragment
| |- DF1 -> DF2 -> DF3
|
|- NotificationHostFragment
|- NF1 -> NF2 -> NF3
首先,为ViewPager2
的每个标签或片段创建导航图
nav_graph_home.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph_dashboard"
app:startDestination="@id/dashboardFragment1">
<fragment
android:id="@+id/dashboardFragment1"
android:name="com.smarttoolfactory.tutorial7_1bnw_viewpager2_nestednavigation.blankfragment.DashboardFragment1"
android:label="DashboardFragment1"
tools:layout="@layout/fragment_dashboard1">
<action
android:id="@+id/action_dashboardFragment1_to_dashboardFragment2"
app:destination="@id/dashboardFragment2" />
</fragment>
<fragment
android:id="@+id/dashboardFragment2"
android:name="com.smarttoolfactory.tutorial7_1bnw_viewpager2_nestednavigation.blankfragment.DashboardFragment2"
android:label="DashboardFragment2"
tools:layout="@layout/fragment_dashboard2">
<action
android:id="@+id/action_dashboardFragment2_to_dashboardFragment3"
app:destination="@id/dashboardFragment3" />
</fragment>
<fragment
android:id="@+id/dashboardFragment3"
android:name="com.smarttoolfactory.tutorial7_1bnw_viewpager2_nestednavigation.blankfragment.DashboardFragment3"
android:label="DashboardFragment3"
tools:layout="@layout/fragment_dashboard3" >
<action
android:id="@+id/action_dashboardFragment3_to_dashboardFragment1"
app:destination="@id/dashboardFragment1"
app:popUpTo="@id/dashboardFragment1"
app:popUpToInclusive="true" />
</fragment>
</navigation>
其他导航图与此图相同
BottomNavigationView的菜单
menu_bottom_nav.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/nav_graph_home"
android:icon="@drawable/ic_baseline_home_24"
android:title="Home"/>
<item
android:id="@+id/nav_graph_dashboard"
android:icon="@drawable/ic_baseline_dashboard_24"
android:title="Dashboard"/>
<item
android:id="@+id/nav_graph_notification"
android:icon="@drawable/ic_baseline_notifications_24"
android:title="Notification"/>
</menu>
ViewPager2适配器
class ActivityFragmentStateAdapter(fragmentActivity: FragmentActivity) :
FragmentStateAdapter(fragmentActivity) {
override fun getItemCount(): Int = 3
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> HomeNavHostFragment()
1 -> DashBoardNavHostFragment()
else -> NotificationHostFragment()
}
}
}
主要活动的布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.ActionBar" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintBottom_toTopOf="@id/bottom_nav"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="@menu/menu_bottom_nav" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
MainActivity在更改标签时都会监听BottomNavigationView
的项目更改和当前NavController
的更改,因为我们必须为每个标签设置Appbar
导航。
class MainActivity : AppCompatActivity() {
// private val appbarViewModel by viewModels<AppbarViewModel>()<AppbarViewModel>()
private val appbarViewModel:AppbarViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val dataBinding: ActivityMainBinding =
DataBindingUtil.setContentView(this, R.layout.activity_main)
val viewPager2 = dataBinding.viewPager
val bottomNavigationView = dataBinding.bottomNav
// Cancel ViewPager swipe
viewPager2.isUserInputEnabled = false
// Set viewpager adapter
viewPager2.adapter = ActivityFragmentStateAdapter(this)
// Listen bottom navigation tabs change
bottomNavigationView.setOnNavigationItemSelectedListener {
when (it.itemId) {
R.id.nav_graph_home -> {
viewPager2.setCurrentItem(0, false)
return@setOnNavigationItemSelectedListener true
}
R.id.nav_graph_dashboard -> {
viewPager2.setCurrentItem(1, false)
return@setOnNavigationItemSelectedListener true
}
R.id.nav_graph_notification -> {
viewPager2.setCurrentItem(2, false)
return@setOnNavigationItemSelectedListener true
}
}
false
}
appbarViewModel.currentNavController.observe(this, Observer { navController ->
navController?.let {
val appBarConfig = AppBarConfiguration(it.graph)
dataBinding.toolbar.setupWithNavController(it, appBarConfig)
}
})
}
}
AppbarViewModel
只有一个MutableLiveData
可以设置当前NavController
。使用ViewModel在NavHost片段中设置ViewModel并能够在Activity或其他片段中获取它的目的。
class AppbarViewModel : ViewModel() {
val currentNavController = MutableLiveData<NavController?>()
}
具有FragmentContainerView的NavHost的布局,当我将工具栏放入这些片段并使用FragmentContainerView
时出现错误,如果将appBar与导航一起使用,则使用fragment
。
fragment_navhost_home.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nested_nav_host_fragment_home"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="false"
app:navGraph="@navigation/nav_graph_home"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
包含子片段和NavController的NavHost片段,其中三个是相同的,所以我只放了一个
class HomeNavHostFragment : BaseDataBindingFragment<FragmentNavhostHomeBinding>() {
override fun getLayoutRes(): Int = R.layout.fragment_navhost_home
private val appbarViewModel by activityViewModels<AppbarViewModel>()
private var navController: NavController? = null
private val nestedNavHostFragmentId = R.id.nested_nav_host_fragment_home
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val nestedNavHostFragment =
childFragmentManager.findFragmentById(nestedNavHostFragmentId) as? NavHostFragment
navController = nestedNavHostFragment?.navController
// Listen on back press
listenOnBackPressed()
}
private fun listenOnBackPressed() {
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback)
}
override fun onResume() {
super.onResume()
callback.isEnabled = true
// Set this navController as ViewModel's navController
appbarViewModel.currentNavController.value = navController
}
override fun onPause() {
super.onPause()
callback.isEnabled = false
}
/**
* This callback should be created with Disabled because on rotation ViewPager creates
* NavHost fragments that are not on screen, destroys them afterwards but it might take
* up to 5 seconds.
*
* ### Note: During that interval touching back button sometimes call incorrect [OnBackPressedCallback.handleOnBackPressed] instead of this one if callback is **ENABLED**
*/
val callback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
// Check if it's the root of nested fragments in this navhost
if (navController?.currentDestination?.id == navController?.graph?.startDestination) {
Toast.makeText(requireContext(), "AT START DESTINATION ", Toast.LENGTH_SHORT)
.show()
/*
Disable this callback because calls OnBackPressedDispatcher
gets invoked calls this callback gets stuck in a loop
*/
isEnabled = false
requireActivity().onBackPressed()
isEnabled = true
} else {
navController?.navigateUp()
}
}
}
}
嵌套导航要注意的重要事项
首先,您需要检查您是否是图形的起始目标,因为您需要调用requireActivity().onBackPressed()
来回叫Activity或例如陷入HomeFragment1的困境
如果在调用requireActivity().onBackPressed()
之前未禁用回调,则会陷入循环,因为onBackPressed还会调用活动回调
如果在当前片段不可见时未禁用callback.isEnabled = false
,则会调用每个回调
最后,我认为最重要的是如果您旋转设备
其他选项卡中的片段也由viewPager创建,然后在3到5时销毁,但是它们的onResume没有被调用,如果创建对象,这会导致其他回调调用handleBackPressed:OnBackPressedCallback( true ),使用
object : OnBackPressedCallback(false)
例如,如果回调处于活动状态,并且您在HomeFragment3
打开时旋转设备,并且在回调处于活动状态时触摸了后退按钮
2020-06-28 13:23:42.722 I: ? HomeNavHostFragment #208670033 onCreate()
2020-06-28 13:23:42.729 I: ⏰ NotificationHostFragment #19727909 onCreate()
2020-06-28 13:23:42.826 I: ? HomeNavHostFragment #208670033 onViewCreated()
2020-06-28 13:23:42.947 I: ⏰ NotificationHostFragment #19727909 onViewCreated()
2020-06-28 13:23:42.987 I: ? HomeNavHostFragment #208670033 onResume()
2020-06-28 13:23:44.092 I: ⏰ NotificationHostFragment #19727909 handleOnBackPressed()
2020-06-28 13:23:44.851 I: ⏰ NotificationHostFragment #19727909 handleOnBackPressed()
2020-06-28 13:23:53.011 I: ⏰ NotificationHostFragment #19727909 onDestroyView()
2020-06-28 13:23:53.023 I: ⏰ NotificationHostFragment #19727909 onDestroy()
即使在HomeFragment3可见时我按了两次返回按钮,也会调用⏰ NotificationHostFragment #19727909 handleOnBackPressed()
,因为ViewPager会创建同样不可见的片段并随后销毁它们。在我的实例中花了10秒钟,您也可以尝试一下。
编辑:建议不要在ViewPager 2的每个片段中使用onBackPressedDispatcher
,而在FragmentStateAdapter
中使用下面的片段,将屏幕上的活动片段设置为 primary导航片段。
/**
* FragmentStateAdapter to add ability to set primary navigation fragment
* which lets fragment visible to be navigable when back button is pressed using
* [FragmentStateAdapter.FragmentTransactionCallback] in [ViewPager2].
*
* * ? Create FragmentStateAdapter with viewLifeCycleOwner instead of Fragment to make sure
* that it lives between [Fragment.onCreateView] and [Fragment.onDestroyView] while [View] is alive
*
* * https://stackoverflow.com/questions/61779776/leak-canary-detects-memory-leaks-for-tablayout-with-viewpager2
*/
abstract class NavigableFragmentStateAdapter(
fragmentManager: FragmentManager,
lifecycle: Lifecycle
) : FragmentStateAdapter(fragmentManager, lifecycle) {
private val fragmentTransactionCallback =
object : FragmentStateAdapter.FragmentTransactionCallback() {
override fun onFragmentMaxLifecyclePreUpdated(
fragment: Fragment,
maxLifecycleState: Lifecycle.State
) = if (maxLifecycleState == Lifecycle.State.RESUMED) {
// This fragment is becoming the active Fragment - set it to
// the primary navigation fragment in the OnPostEventListener
OnPostEventListener {
fragment.parentFragmentManager.commitNow {
setPrimaryNavigationFragment(fragment)
}
}
} else {
super.onFragmentMaxLifecyclePreUpdated(fragment, maxLifecycleState)
}
}
init {
// Add a FragmentTransactionCallback to handle changing
// the primary navigation fragment
registerFragmentTransactionCallback()
}
fun registerFragmentTransactionCallback() {
registerFragmentTransactionCallback(fragmentTransactionCallback)
}
fun unregisterFragmentTransactionCallback() {
unregisterFragmentTransactionCallback(fragmentTransactionCallback)
}
}
这里是link for full sample。您还可以将Toolbar放入每个navHost片段,这有点简单。
您使用工具栏调用NavHost片段
val appBarConfig = AppBarConfiguration(navController!!.graph)
dataBinding.toolbar.setupWithNavController(navController!!, appBarConfig)
答案 2 :(得分:0)
我们可以轻松使用底部导航组件和NavigationGraph来实现。
您应该为每个底部导航菜单创建相应的片段
nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/actionHome">
<fragment
android:id="@+id/actionHome"
android:name="com.sample.demo.fragments.Home"
android:label="fragment_home"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/toExplore"
app:destination="@id/actionExplore" />
</fragment>
<fragment
android:id="@+id/actionExplore"
android:name="com.sample.demo.fragments.Explore"
android:label="fragment_explore"
tools:layout="@layout/fragment_explore" />
<fragment
android:id="@+id/actionBusiness"
android:name="com.sample.demo.fragments.Business"
android:label="fragment_business"
tools:layout="@layout/fragment_business" />
<fragment
android:id="@+id/actionProfile"
android:name="com.sample.demo.fragments.Profile"
android:label="fragment_profile"
tools:layout="@layout/fragment_profile" />
</navigation>
每个导航片段ID和底部导航菜单项ID都应该相同。例如此处
<fragment
android:id="@+id/actionBusiness"
android:name="com.sample.demo.fragments.Business"
android:label="fragment_business"
tools:layout="@layout/fragment_business" />
在底部导航菜单Navigation.xml下方
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/actionExplore"
android:icon="@drawable/ic_search_24dp"
android:title="@string/explore" />
<item
android:id="@+id/actionBusiness"
android:icon="@drawable/ic_business_24dp"
android:title="@string/business" />
<item
android:id="@+id/actionProfile"
android:icon="@drawable/ic_profile_24dp"
android:title="@string/profile" />
</menu>
将nav_graph.xml设置为activity_main.xml中的palceholder片段
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/gradient_bg"
android:focusable="true"
android:focusableInTouchMode="true"
tools:context=".MainActivity"
tools:layout_editor_absoluteY="25dp">
<android.support.design.widget.BottomNavigationView
android:id="@+id/navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="@color/semi_grey"
app:itemIconTint="@drawable/bottom_bar_nav_item"
app:itemTextColor="@drawable/bottom_bar_nav_item"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/navigation" />
<include
android:id="@+id/appBarLayout"
layout="@layout/app_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<fragment
android:id="@+id/mainNavigationFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingBottom="@dimen/activity_horizontal_margin"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/navigation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBarLayout"
app:navGraph="@navigation/nav_graph" />
</android.support.constraint.ConstraintLayout>
在此处将导航图映射为片段 app:navGraph =“ @ navigation / nav_graph”
之后,在MainActivity.java中实现导航图和bottomNavigation组件
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
NavController navController = Navigation.findNavController(this, R.id.mainNavigationFragment);
NavigationUI.setupWithNavController(navigation, navController);
干杯!
答案 3 :(得分:0)
对我来说,一个解决方案是将ViewPager中的片段放在导航之外,而直接在页面片段上设置操作,就好像这些页面是宿主一样。 为了更好地解释它:
假设您位于片段A中,而ViewPager为片段B 然后您尝试从B导航到C
在片段B中,使用ADirections类以及从A到C的操作。findNavHost()。navigateTo(ADirections.ActionFromAtoC)
答案 4 :(得分:0)
我写了a related article on this regarding view pagers,特别关注标签中的主从片段,但相同的逻辑适用于常规ViewPagers。代码是located here。
答案 5 :(得分:0)
我有一个MainFragment,它在viewPager中托管片段A,片段B和片段C。
我想从片段B(由MainFragment内部的viewPager托管)打开片段D。
所以我创建了一个从MainFragment到Fragment D的动作,并从Fragment B调用
val direction = FragmentMainDirections.actionFragmentMainToFragmentD()
findNavController().navigate(direction)
工作。
答案 6 :(得分:0)
除了要让Marat在每个片段中使用后退按钮工作的答案外,您还必须将其添加到onViewCreated的容器片段中:
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (!navHostFragment.navController.popBackStack()) {
isEnabled = false
activity?.onBackPressed()
}
}
}
activity?.onBackPressedDispatcher?.addCallback(this, callback)
答案 7 :(得分:0)
感谢@Marat-他提供了很好的解决方案。 就我而言,我在第二个ViewPager的视图中具有列表/详细视图导航,并使用 Fullscreeen模式,而没有任何Action \ Tool-bar :
想评论一下:
1)我可以在一张页面上使用一个普通图形:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/page2Coordinator"
app:startDestination="@id/Fragment_2Coordinator">
<fragment
android:id="@+id/Fragment_2Coordinator"
android:name="my.app.Fragment_2Coordinator"
android:label="Fragment_2Coordinator">
<action
android:id="@+id/action_showList_2A"
app:destination="@id/Fragment_2A" />
</fragment>
<fragment
android:id="@+id/Fragment_2A"
android:name="my.app.Fragment_2A"
android:label="Fragment_2A">
<action
android:id="@+id/action_goToDetail_2B"
app:destination="@id/Fragment_2B" />
</fragment>
<fragment
android:id="@+id/Fragment_2B"
android:name="my.app.Fragment_2B"
android:label="Fragment_2B">
<action
android:id="@+id/action_backToList_2A"
app:destination="@id/Fragment_2A" />
</fragment>
</navigation>
2)而不是使用Fragment_2Coordinator.onViewCreated()中的工具栏进行操作,只需在图形中进行操作即可导航(以防不使用系统导航):
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val navHostFragment = childFragmentManager.findFragmentById(R.id. tab_1_nav_host_fragment) as NavHostFragment? ?: return
val navController = navHostFragment.navController
navController.navigate(R.id.action_showList_2A)
}
3)要使用电话“返回”按钮从2B返回2A,请转到“活动”:
class MainActivity : AppCompatActivity() {
. . . . .
override fun onBackPressed() {
val navController = findNavController(R.id.tab_1_nav_host_fragment)
when(navController.currentDestination?.id) {
R.id.Fragment_2B -> {
navController.navigate(R.id.action_backToList_2A)
}
else -> {
super.onBackPressed()
}
}
println()
}
}
答案 8 :(得分:-1)
我已经使用viewpager实现了 Android Arch导航。请看一看。任何改进都欢迎。让我们学习聚会。
https://github.com/Maqsood007/AndroidJetpack/tree/master/ArchNavViewPagerImpl