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 即可。

相关推荐
_一条咸鱼_1 天前
大厂Android面试秘籍:Activity 结果回调处理(八)
android·面试·android jetpack
_一条咸鱼_1 天前
大厂Android面试秘籍:Activity 与 Fragment 交互(九)
android·面试·android jetpack
_一条咸鱼_2 天前
大厂Android面试秘籍:Activity 权限管理模块(七)
android·面试·android jetpack
harry235day2 天前
Compose 自定义转盘
kotlin·android jetpack
砖厂小工2 天前
使用 DAG (有向无环图)管理复杂依赖
算法·android jetpack
_一条咸鱼_3 天前
大厂Android面试秘籍:Activity 窗口管理模块(四)
android·面试·android jetpack
_一条咸鱼_3 天前
Android大厂面试秘籍:不同Android系统版本特性分析
android·面试·android jetpack
_一条咸鱼_3 天前
Android大厂面试秘籍: Activity Intent 解析与处理模块(三)
android·面试·android jetpack
_一条咸鱼_4 天前
Android 大厂面试秘籍:Activity 生命周期原理详解
android·面试·android jetpack