简体   繁体   English

如何向片段添加底部导航侦听器

[英]How to add bottom navigation listeners to fragments

I used to have a single activity that included the navigation host and the BottomNavigationView that allowed switching views between the 3 fragments.我曾经有一个单独的活动,其中包括导航主机和允许在 3 个片段之间切换视图的BottomNavigationView导航视图。 I recently changed my design so that the initial activity now represents a single new fragment, and there exists a button that allows the user to navigate to another fragment which should hold the aforementioned 3 tabbed bottom navigation.我最近更改了我的设计,以便初始活动现在代表一个新的片段,并且存在一个按钮,允许用户导航到另一个片段,该片段应包含上述 3 选项卡式底部导航。

I've managed to implement the initial activity and the first fragment.我设法实现了初始活动和第一个片段。 But the issue that I am having is that when I navigate to the second fragment with the 3 tabbed bottom navigation bar, I don't know how to implement the onClickListener on the tabs.但是我遇到的问题是,当我导航到带有 3 个选项卡式底部导航栏的第二个片段时,我不知道如何在选项卡上实现onClickListener It used to be fairly straightforward when I had the bottom navigation inside the AppCompatActivity .当我在AppCompatActivity有底部导航时,它曾经相当简单。 I'm guessing that fragments are not supposed to be used for this case.我猜片段不应该用于这种情况。 I could easily use another activity class but I wanted to build a single activity application and wondered if there was another solution to this.我可以轻松地使用另一个活动类,但我想构建一个单一的活动应用程序,并想知道是否有其他解决方案。

In the original method using an activity class, I was able to implement the bottom navigation like below:在使用活动类的原始方法中,我能够实现如下底部导航:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)
        val navController = Navigation.findNavController(this, R.id.nav_host_fragment)

        setupBottomNavMenu(navController)
        setupActionBar(navController)
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.menu_toolbar, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
        val navController = Navigation.findNavController(this, R.id.nav_host_fragment)
        val navigated = NavigationUI.onNavDestinationSelected(item!!, navController)
        return navigated || super.onOptionsItemSelected(item)
    }

    override fun onSupportNavigateUp(): Boolean {
        return NavigationUI.navigateUp(Navigation.findNavController(this, R.id.nav_host_fragment), drawer_layout)
    }

    private fun setupBottomNavMenu(navController: NavController) {
        bottom_nav?.let {
            NavigationUI.setupWithNavController(it, navController)
        }
    }

    private fun setupActionBar(navController: NavController) {
        NavigationUI.setupActionBarWithNavController(this, navController, drawer_layout)
    }
}

<?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"
    xmlns:tools="http://schemas.android.com/tools">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="@color/colorMintCream"
        tools:context=".activity.MainActivity">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?colorPrimary"
            android:theme="@style/ToolbarTheme"/>

        <fragment
            android:id="@+id/nav_host_fragment"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:name="androidx.navigation.fragment.NavHostFragment"
            app:navGraph="@navigation/main_nav_graph"
            app:defaultNavHost="true"/>

       <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottom_nav"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:menu="@menu/menu_navigation"/>

    </LinearLayout>
</layout>

The navigation looks like follows:导航如下所示:

<?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" android:id="@+id/mobile_navigation.xml"
    app:startDestination="@id/destination_profile">

    <fragment
        android:id="@+id/destination_profile"
        android:name="com.example.project.profile.ProfileFragment"
        android:label="Profile" />
    <fragment
        android:id="@+id/destination_achievements"
        android:name="com.example.project.fragments.AchievementsFragment"
        android:label="Achievements" />
    <fragment
        android:id="@+id/destination_logs"
        android:name="com.example.project.fragments.LogsFragment"
        android:label="Logs" />
    <fragment
        android:id="@+id/destination_settings"
        android:name="com.example.project.fragments.SettingsFragment"
        android:label="Settings" />
</navigation>

And finally the menu:最后是菜单:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/destination_profile"
        android:icon="@drawable/ic_person_black_24dp"
        android:title="@string/profile"/>

    <item
        android:id="@+id/destination_logs"
        android:icon="@drawable/ic_fitness_center_black_24dp"
        android:title="@string/logs"/>

    <item
        android:id="@+id/destination_achievements"
        android:icon="@drawable/ic_star_black_24dp"
        android:title="@string/achievements"/>
</menu>

Similarly I tried to apply this on a fragment like below:同样,我尝试将其应用于如下片段:


class MainFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater,
                              container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {

        var binding: FragmentMainBinding = DataBindingUtil.inflate(
                inflater,
                R.layout.fragment_main,
                container,
                false
        )

        val application = requireNotNull(this.activity).application
        val dataSource = UserProfileDatabase.getInstance(application).userProfileDao
        val viewModelFactory = MainViewModelFactory(dataSource, application)
        val navController = this.findNavController()
        NavigationUI.setupWithNavController(binding.bottomNav, navController)
        return binding.root
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        super.onCreateOptionsMenu(menu, inflater)
        inflater.inflate(R.menu.menu_toolbar, menu)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        val navController = this.findNavController()
        val navigated = NavigationUI.onNavDestinationSelected(item, navController)
        return navigated || super.onOptionsItemSelected(item)
    }

    companion object {
        fun newInstance(): MainFragment = MainFragment()
    }
}

The xml is almost exactly the same as the one for activity. xml 几乎与活动的 xml 完全相同。

I expected to invoke onOptionsItemSelected() when I clicked on the tab elements, but I wasn't able to do so.我希望在单击选项卡元素时调用onOptionsItemSelected() ,但我无法这样做。 How can I implement the listeners on those tab elements so that I can navigate to the correct fragment destinations?如何在这些选项卡元素上实现侦听器,以便我可以导航到正确的片段目的地?

Why don't you keep your first implementation for your BottomNavigationView which is good and combine it with your new implementation by using addOnDestinationChangedListener . 您为什么不保留良好的BottomNavigationView第一个实现,并通过使用addOnDestinationChangedListener将其与新实现结合起来。

Your FirstFragment that hold the button will hide the BottomNavigationView and the SecondFragment with the 3 tabbed bottom navigation will show it. FirstFragment持有该按钮将隐藏BottomNavigationViewSecondFragment用3个标签底部导航将显示它。

For example : 例如 :

navController.addOnDestinationChangedListener { _, destination, _ ->

 if(destination.id == R.id.first_fragment) {
    // your intro fragment will hide your bottomNavigationView
    bottomNavigationView.visibility = View.GONE
  } else if (destination.id == R.id.second_fragment){
   // your second fragment will show your bottomNavigationView
   bottomNavigationView.visibility = View.VISIBLE
 }
}

in that way you keep the control in your Activity instead of Fragment and by that interact better with your listeners and your NavController . 这样,您可以将控件保留在Activity中而不是Fragment中,从而更好地与您的侦听器和NavController

Keep your menu item's id same as your graph id and simply use 1 line code ie val navHostFragment =childFragmentManager.findFragmentById(R.id.fragmentContainerView) as NavHostFragment保持菜单项的 id 与图形 id 相同,只需使用 1 行代码,即 val navHostFragment =childFragmentManager.findFragmentById(R.id.fragmentContainerView) as NavHostFragment

    val navController = navHostFragment.navController

    binding.bottomNavBar.setupWithNavController(navController)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM