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