如何使用新的导航体系结构组件用BottomNavigationView实现ViewPager? [英] How to implement a ViewPager with BottomNavigationView using new Navigation Architecture Component?

查看:707
本文介绍了如何使用新的导航体系结构组件用BottomNavigationView实现ViewPager?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个具有BottomNavigationViewViewPager的应用程序. 如何使用新的导航架构组件"实现它?

I have an application with a BottomNavigationView and ViewPager. How is it possible to implement it using new "Navigation Architecture Component?"

最佳做法是什么?

非常感谢

推荐答案

使用导航Arch Component的BottomNavigationView的默认实现对我来说不可行.单击选项卡时,它会根据导航图从头开始.

Default implementation of BottomNavigationView with Navigation Arch Component didn't work out for me. When clicking on tabs it starts them from beginning according to navigation graph.

我需要在屏幕底部拥有5个选项卡,并且每个选项卡都有一个单独的后置堆栈.这意味着在标签之间进行切换时,您将始终返回到离开前的状态(就像在Instagram中一样).

I need to have 5 tabs in the bottom of the screen and have a separate backstack for each of the tabs. Which means when switching between tabs you will always return to the exactly the same state as it was before leaving (like in Instagram).

我的方法如下:

  1. ViewPagerBottomNavigationView放入activity_main.xml
  2. MainActivity.kt中将OnNavigationItemSelectedListener设置为BottomNavigationView
  3. 为每个选项卡创建单独的容器片段(它们将是每个选项卡的起点)
  4. 在容器片段的xml中
  5. 包括NavHostFragment.
  6. 在每个Container片段中为Navigation Arch Component实施必要的代码.
  7. 为每个标签创建图形
  1. Put ViewPager and BottomNavigationView in activity_main.xml
  2. Set OnNavigationItemSelectedListener to BottomNavigationView in MainActivity.kt
  3. Create separate Container fragments for each of the tabs (they will be the starting point of each tab)
  4. include NavHostFragment inside of Container fragments' xml.
  5. Implement necessary code for Navigation Arch Component in each of the Container fragments.
  6. Create a graph for each of the tabs

注意:每个图都可以彼此交互.

Note: each of the graphs can interact with each other.

重要的一点是,我们将工具栏不是放置在活动中,而是放置在容器"片段中.然后,我们在工具栏本身上调用setupWithNavController()而不将其设置为supportActionBar.这样,工具栏标题将被自动更新,并且上一步/上一步按钮将被自动管理.

Important point here is that we place Toolbar not in activity but in Container fragment. Then we call setupWithNavController() on toolbar itself without setting it as supportActionBar. This way toolbar titles will be automatically updated and Back/Up button will be managed automatically.

结果:

  • ViewPager存储每个选项卡的状态.
  • 不用担心碎片交易.
  • SafeArgsDeepLinking可以正常工作.
  • 我们对BottomNavigationManagerViewPager拥有完全控制权(即,我们可以实现OnNavigationItemReselectedListener并决定在弹出后退堆栈之前将当前选项卡中的列表滚动到顶部).
  • ViewPager stored states of each tabs.
  • Didn't worry about fragment transactions.
  • SafeArgs and DeepLinking works as expected.
  • We have full control over BottomNavigationManager and ViewPager (i.e. we can implement OnNavigationItemReselectedListener and decide to scroll lists in current tab to top before popping backstack).

代码:

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

我们可以根据需要创建任意数量的导航图:

We can create as many navigation graphs as you want:

但是我们需要为每个标签创建一个单独的图形:

But we need to have a separate graph for each tabs:

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

此处的起始目标片段是您想在标签中的第一个屏幕上显示的任何片段.

Here start destination fragment is any fragment you want to appear as first screen in tab.

这篇关于如何使用新的导航体系结构组件用BottomNavigationView实现ViewPager?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆