Navigation的使用

Android 之神 Jake Wharton 曾建议一个 App 只需要一个 Activity 即可,说的就是单 Activity 多 Fragment 的模式。以前在 Fragment 之间跳转比较麻烦,现在有了 Navigation,就变得很容易了。Navigation 是 Jetpack 的一部分,官网地址为:developer.android.com/guide/navig...

简单使用

在 build.gradle.kts 中添加如下依赖:

kotlin 复制代码
val navVersion = "2.6.0"
implementation("androidx.navigation:navigation-fragment-ktx:$navVersion")
implementation("androidx.navigation:navigation-ui-ktx:$navVersion")

选择 New > Android Resource File, Resource Type 选择 navigation,文件名为 nav,选择 OK,即在 res/navigation/ 目录下生成了一个 nav.xml 文件。进入 nav.xml 的可视化界面,提示:click to add a destination,点击该按钮后会显示 Activity 和 Fragment 的列表。我的 App 里面有 MainActivity、SecondActivity、FirstFragment 和 SecondFragment,我把它们全部添加进来。

当把鼠标移动 Fragment 上时,会发现右边出现了一个圆圈,Activity 则不会出现这个圆圈。拖动这个圆圈指向另一个 Fragment 或 Activity 后会出现一个箭头,一个箭头表示一个跳转行为,称为 Action。Activity 的右边没有这个圆圈,所以 Navigation 并不能处理从 Activity 发起的跳转。

这里给 FirstFragment 添加了两个 Action,分别指向 SecondActivity 和 SecondFragment。由于 MainActivity 与它们都没有跳转行为,我们直接从 nav.xml 中删除。

再来查看 nav.xml,发现代码变成了如下所示:

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"
    android:id="@+id/nav"
    app:startDestination="@id/firstFragment">

    <fragment
        android:id="@+id/firstFragment"
        android:name="com.example.navigation.FirstFragment"
        android:label="FirstFragment" >
        <action
            android:id="@+id/action_firstFragment_to_secondActivity"
            app:destination="@id/secondActivity" />
        <action
            android:id="@+id/action_firstFragment_to_secondFragment"
            app:destination="@id/secondFragment" />
    </fragment>
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.example.navigation.SecondFragment"
        android:label="SecondFragment" />
    <activity
        android:id="@+id/secondActivity"
        android:name="com.example.navigation.SecondActivity"
        android:label="SecondActivity" />
</navigation>

上图中 FirstFragment 的左上角有个 标志,代表这个是入口 Fragment,对应 xml 代码中的 startDestination 属性,从 xml 代码中也可以看到 firstFragment 的两个 Action,一个指向 secondActivity,一个指向 secondFragment。

我们把 MainActivity 作为入口的 Activity,activity_main.xml 代码如下:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">

    <fragment
        android:id="@+id/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/nav" />

</FrameLayout>

这里 fragment 标签中的配置就跟以前不一样了:

  • name="androidx.navigation.fragment.NavHostFragment"。
  • navGraph="@navigation/nav",指向我们添加的 nav.xml 文件。
  • defaultNavHost="true",其作用是让 Navigation 处理返回事件来返回上一个页面,上一个页面有可能是 Activity,也有可能是 Fragment。

在 Navigation 中,页面的跳转是交给 NavController 来处理的,获取 NavController 的方法有以下三种:

  • NavHostFragment.findNavController(Fragment)。
  • Navigation.findNavController(Activity, @IdRes int viewId)。
  • Navigation.findNavController(View)。

拿到 NavController 后调用其 navigate() 方法即可实现跳转,在 FirstFragment 中添加如下代码:

kotlin 复制代码
binding.btnJump1.setOnClickListener{
    NavHostFragment
        .findNavController(this)
        .navigate(R.id.action_firstFragment_to_secondActivity)
}
binding.btnJump2.setOnClickListener {
    NavHostFragment
        .findNavController(this)
        .navigate(R.id.action_firstFragment_to_secondFragment)
}

这样即可通过 btnJump1 和 btnJump2 跳转到 SecondActivity 和 SecondFragment。

还可以通过 Bundle 来传参,代码如下:

kotlin 复制代码
val bundle = Bundle() bundle.putString("name", "Ray") 
NavHostFragment
    .findNavController(this) 
    .navigate(R.id.action_firstFragment_to_secondFragment, bundle)

页面切换动画

使用 Navigation 可以很方便地实现页面切换动画,首先准备两个动画文件:

slide_out_left.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="600"
        android:fromXDelta="100%"
        android:toXDelta="0" />
</set>

slide_in_right.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?> 
<set xmlns:android="http://schemas.android.com/apk/res/android"> 
    <translate 
       android:duration="600" 
       android:fromXDelta="100%" 
       android:toXDelta="0" /> 
</set>

然后打开 nav.xml 切换到可视化界面,点击跳转的箭头,把两个动画填进去就可以了,如下所示:

点击 btnJump1 会发现页面切换动画已经添加好了。

底部导航

Navigation 也可以结合 BottomNavigationView 实现微信的底部菜单导航的效果,activity_main.xml 代码如下:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?android:attr/windowBackground"
        android:layout_alignParentBottom="true"
        app:itemIconTint="@drawable/selector_bottom_nav_colors"
        app:itemTextColor="@drawable/selector_bottom_nav_colors"
        app:menu="@menu/menu" />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/bottom_nav_view"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav" />

</RelativeLayout>

里面添加了一个 BottomNavigationView ,对应的 menu 代码为:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/firstFragment"
        android:icon="@drawable/ic_home_black_24dp"
        android:title="First"/>

    <item
        android:id="@+id/secondFragment"
        android:icon="@drawable/ic_dashboard_black_24dp"
        android:title="Second"/>

    <item
        android:id="@+id/thirdFragment"
        android:icon="@drawable/ic_notifications_black_24dp"
        android:title="Third"/>

</menu>

注意 menu 里面 item 的 id 要与 nav.xml 中 Fragment 的 id 对应,MainActivity 的代码如下:

kotlin 复制代码
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        val controller = navHostFragment.navController
        NavigationUI.setupWithNavController(binding.bottomNavView, controller)
    }
}

这样就可以通过点击底部的菜单实现 Fragment 的切换了,如下图所示:

其实 Android Studio 可以自动帮我们实现底部导航效果,只需要在新建项目的时候选择 Bottom Navigation Views Activity 即可。

相关推荐
消失的旧时光-194312 小时前
Kotlinx.serialization 使用讲解
android·数据结构·android jetpack
Tans518 小时前
Androidx Fragment 源码阅读笔记(下)
android jetpack·源码阅读
Lei活在当下2 天前
【业务场景架构实战】2. 对聚合支付 SDK 的封装
架构·android jetpack
Tans54 天前
Androidx Fragment 源码阅读笔记(上)
android jetpack·源码阅读
alexhilton5 天前
runBlocking实践:哪里该使用,哪里不该用
android·kotlin·android jetpack
Tans58 天前
Androidx Lifecycle 源码阅读笔记
android·android jetpack·源码阅读
ljt27249606619 天前
Compose笔记(四十九)--SwipeToDismiss
android·笔记·android jetpack
4z3311 天前
Jetpack Compose重组优化:机制剖析与性能提升策略
性能优化·android jetpack
alexhilton11 天前
Android ViewModel数据加载:基于Flow架构的最佳实践
android·kotlin·android jetpack
水牛15 天前
一行代码完成startActivityForResult
android·android jetpack