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

相关推荐
alexhilton3 天前
Kotlin互斥锁(Mutex):协程的线程安全守护神
android·kotlin·android jetpack
是六一啊i5 天前
Compose 在Row、Column上使用focusRestorer修饰符失效原因
android jetpack
用户060905255226 天前
Compose 主题 MaterialTheme
android jetpack
用户060905255226 天前
Compose 简介和基础使用
android jetpack
用户060905255226 天前
Compose 重组优化
android jetpack
行墨6 天前
Jetpack Compose 深入浅出(一)——预览 @Preview
android jetpack
alexhilton7 天前
突破速度障碍:非阻塞启动画面如何将Android 应用启动时间缩短90%
android·kotlin·android jetpack
Pika8 天前
深入浅出 Compose 测量机制
android·android jetpack·composer
fundroid10 天前
掌握 Compose 性能优化三步法
android·android jetpack