android - 如何使用新的导航架构组件实现带有 BottomNavigationView 的 ViewPager?

标签 android android-architecture-components

我有一个带有 BottomNavigationViewViewPager 的应用程序。 如何使用新的“导航架构组件”来实现它?

最佳做法是什么?

非常感谢

最佳答案

更新(21 年 6 月 15 日):

从导航组件版本 2.4.0-alpha01 开始开箱即用支持多个后堆栈。根据文档,如果您将 NavigationViewBottomNavigationView 与 Navigation 组件一起使用,则多个后退堆栈应该可以工作,而无需对先前的实现进行任何代码更改。

As part of this change, the NavigationUI methods of onNavDestinationSelected(), BottomNavigationView.setupWithNavController() and NavigationView.setupWithNavController() now automatically save and restore the state of popped destinations, enabling support for multiple back stacks without any code changes. When using Navigation with Fragments, this is the recommended way to integrate with multiple back stacks.

原答案:

BottomNavigationView 与 Navigation Arch 组件的默认实现对我来说不起作用。单击选项卡时,它会根据导航图从头开始。

我需要在屏幕底部有 5 个选项卡,并且每个选项卡都有一个单独的后退堆栈。这意味着在标签之间切换时,您将始终返回与离开前完全相同的状态(如在 Instagram 中)。

我的做法如下:

  1. ViewPagerBottomNavigationView放入activity_main.xml
  2. MainActivity.kt
  3. 中将 OnNavigationItemSelectedListener 设置为 BottomNavigationView
  4. 为每个选项卡创建单独的容器 fragment (它们将是每个选项卡的起点)
  5. 在容器 fragment 的 xml 中包含 NavHostFragment
  6. 在每个 Container fragment 中为 Navigation Arch 组件实现必要的代码。
  7. 为每个选项卡创建图表

注意:每个图表都可以相互交互。

这里重要的一点是,我们将工具栏不是放在 Activity 中,而是放在容器 fragment 中。然后我们在工具栏本身上调用 setupWithNavController() 而不将其设置为 supportActionBar。这样工具栏标题将自动更新,Back/Up按钮将自动管理。

结果:

  • ViewPager 存储了每个选项卡的状态。
  • 不用担心 fragment 交易。
  • SafeArgsDeepLinking 按预期工作。
  • 我们可以完全控制 BottomNavigationManagerViewPager(即我们可以实现 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 graphs

但我们需要为每个选项卡创建一个单独的图表:

<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>

此处的起始目标 fragment 是您希望在选项卡中显示为第一个屏幕的任何 fragment 。

关于android - 如何使用新的导航架构组件实现带有 BottomNavigationView 的 ViewPager?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52630267/

相关文章:

带有 ViewPager 和 TabLayout 的 Android Jetpack 导航

android - 通过向查询添加监听器返回的 DataSnapshot 是否不同于直接添加到 DatabaseReference 的 DataSnapshot

android - 运行 Espresso 测试时的 java.lang.NoClassDefFoundError : android. databinding.DataBinderMapperImpl

java - 静态变量在android应用程序中的 Activity 中真的安全吗

android - 房间持久性库。删除所有

android - MVVM中 View 和ViewModel之间的通信与LiveData

android - 如何授予我的 Android 应用管理员权限?

android - JavaCV - 渲染到 GL 表面

Android WorkManager - CoroutineWorker : Overriding coroutineContext is deprecated

android - 如何从 Android MVVM 架构中的 LiveData 列表中获取第一个或(任何)元素?