BottomNavigationView(Navigation)的简单使用(MVVM+retrofit+协程+Hilt+组件化)

最近整理项目框架,决定使用BottomNavigationView+Navigation来完成主页,碰到了一系列问题,贡献出摸索过程,供需要的宝宝参考。大体效果如下图:
在这里插入图片描述

一、准备

implementation "androidx.navigation:navigation-fragment-ktx:2.3.0"
implementation "androidx.navigation:navigation-ui-ktx:2.3.0"

二、首先解决BottomNavigationView出现的几个问题

  1. 切换条目出现缩放位移动画
    ·添加app:labelVisibilityMode="labeled"
  2. 点击item怎么去掉水波纹动画
    ·添加app:itemBackground="@null"
  3. 自定义item图片后没有显示自己想要的图片
    ·activity中设置bottom_nav.itemIconTintList = null
  4. 字体大小怎么修改
    ·dimen文件中直接覆盖
<dimen name="design_bottom_navigation_active_text_size" tools:override="true">12sp</dimen>
<dimen name="design_bottom_navigation_text_size" tools:override="true">12sp</dimen>

三、实现

这种底部导航有两种实现方式。

第一种简单方式,直接使用fragment节点
<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">

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

<com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?android:attr/windowBackground"
        app:itemBackground="@null"
        app:itemIconTint="@null"
        app:itemTextColor="@color/selector_main_color"
        app:labelVisibilityMode="labeled"
        app:menu="@menu/bottom_nav" />
</LinearLayout>

这里需要注意的是bottom_nav中item的id要同导航文件中fragment的id相同,否则找不到id不能切换页面

在activity文件中添加一行代码就能顺利切换页面了

bottom_nav.setupWithNavController(findNavController(R.id.nav_host_fragment))

但 是 , 使 用 这 种 方 式 切 换 页 面 有 两 个 缺 点 : \color{#FF0000}{但是,使用这种方式切换页面有两个缺点:} 使
1.每次切换页面都重新走一遍fragment生命周期
2.页面点击返回后返回到了navigation中设置的startDestination,但是我想要的是在任一页面按返回都直接退出APP

然后,就出现了第二种方式,使用FragmentContainerView
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_container"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

 <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?android:attr/windowBackground"
        app:itemBackground="@null"
        app:itemIconTint="@null"
        app:itemTextColor="@color/selector_main_color"
        app:labelVisibilityMode="labeled"
        app:menu="@menu/bottom_nav" />
</LinearLayout>

这种方式需要自己写切换页面逻辑,也就是使用fragmentTransaction的show和hide方法,这样,fragment生命周期就不会每次都重新走一遍了,在任一页面退出都能退出APP。

切换逻辑如下:(注意别忘了每个导航页面的id要同menu中的item id保持一致)

fun BottomNavigationView.setupWithNavController(
    navGraphIds: List<Int>,
    fragmentManager: FragmentManager,
    containerId: Int
): LiveData<NavController> {
    // Result. Mutable live data with the selected controlled
    val selectedNavController = MutableLiveData<NavController>()
    if (navGraphIds.isEmpty()) return selectedNavController
    var selectedItemTag = getFragmentTag(selectedItemId)

    fragmentManager.beginTransaction()
        .add(containerId, NavHostFragment.create(navGraphIds[0]), selectedItemTag)
        .commitNowAllowingStateLoss()
    setOnNavigationItemSelectedListener { item ->
        if (fragmentManager.isStateSaved) {
            false
        } else {
            val newlySelectedTag = getFragmentTag(item.itemId)
            if (selectedItemTag != newlySelectedTag) {
                val selectedFragment =
                    fragmentManager.findFragmentByTag(selectedItemTag) as NavHostFragment?
                val fragmentTransaction = fragmentManager.beginTransaction()
                selectedFragment?.let { fragmentTransaction.hide(it) }

                var newlySelectedFragment =
                    fragmentManager.findFragmentByTag(newlySelectedTag) as NavHostFragment?
                if (null == newlySelectedFragment) {
                    var index = 0
                    menu.forEachIndexed { index1, item1 ->
                        if (item1 == item) {
                            index = index1
                            return@forEachIndexed
                        }
                    }
                    newlySelectedFragment = NavHostFragment.create(navGraphIds[index])
                    fragmentTransaction
                        .setCustomAnimations(
                            R.anim.nav_default_enter_anim,
                            R.anim.nav_default_exit_anim,
                            R.anim.nav_default_pop_enter_anim,
                            R.anim.nav_default_pop_exit_anim
                        )
                        .add(containerId, newlySelectedFragment!!, newlySelectedTag)
                        .commitNowAllowingStateLoss()
                } else {
                    fragmentTransaction.show(newlySelectedFragment).commitNowAllowingStateLoss()
                }
                selectedItemTag = newlySelectedTag
                selectedNavController.value = selectedFragment?.navController
                true
            } else {
                false
            }
        }
    }
    return selectedNavController
}

activity直接设置即可

        val navGraphIds = listOf(
            R.navigation.home,
            R.navigation.sort,
            R.navigation.find,
            R.navigation.cart,
            R.navigation.mine
        )
        bottom_nav.setupWithNavController(
            navGraphIds = navGraphIds,
            fragmentManager = supportFragmentManager,
            containerId = R.id.nav_host_container
        )

此文只是简单使用navigation实现底部导航栏小功能。导航组件还有很多需要学习的地方,但是也有很多需要改进的地方,希望不久的将来我们都能开发出单activity的APP。
另外,整理项目框架的时候顺便从dagger2迁移到了hilt,虽然hilt还是开发版本。hilt确实比dagger使用方便了很多,但是要理解透彻也是不太容易,毕竟依赖注入对于很多人而言就已经很难理解了 (T_T)
MVVM+ViewModel+Databinding+retrofit+rxJava+BottomNavigationView简单框架

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值