一直以来我写的 Android App 都是用一个 Activity 承接一个页面的业务逻辑,代码越写越多,各种 Activity 之间的跳转也非常混乱了,甚至到了难以维护的程度。

借着这个学习的机会,我觉得自己是应该好好了解一下 Android Navigation 了,这篇博客主要参考 Android Developer Website 的文档,当然还有我学习过程中踩到的坑的一个记录。最后就是对于 Navigation + BottomNavigationView 的一个综合使用过程。为之后的 Android 应用架构搭建打下基础,与君共勉。

首先在使用 Navigation 之前得先了解一下 Navigation 能干什么,我要用它来达到什么目的。

目前来说,我的需求主要是各各 Activity 之间跳转混乱,使用 Navigation 能够实现一个 Activity 多 Fragment 的架构。方便统一管理页面,修改页面实现不影响路由跳转。

首先将依赖加入到项目的 app/build.gradle 中

dependencies {
  def nav_version = "2.3.5"

  // Java language implementation
  implementation "androidx.navigation:navigation-fragment:$nav_version"
  implementation "androidx.navigation:navigation-ui:$nav_version"

  // Kotlin
  implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
  implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

  // Feature module Support
  implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"

  // Testing Navigation
  androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"

  // Jetpack Compose Integration
  implementation "androidx.navigation:navigation-compose:2.4.0-alpha10"
}

然后我们可以使用 Android Studio 来创建 navigation 资源文件,具体操作步骤如下

我觉得,是时候该好好聊聊 Android Navigation 了。-天真的小窝

选择 res 文件夹 -> New -> Android Resource File

我觉得,是时候该好好聊聊 Android Navigation 了。-天真的小窝

File name 处输入文件名,我这里就参考官网文档输入 nav_graph ,Resource type 选择 Navigation,然后点击 OK 创建资源文件即可。

我觉得,是时候该好好聊聊 Android Navigation 了。-天真的小窝

创建成功后我们能看到 res 文件夹下多了一个 navigation 文件夹和 nav_graph.xml 资源文件,打开 nav_graph.xml 文件点击 Design 就能直接使用 Android studio 的 Navigation Editor 工具,提供了非常方便的可视化编辑工具(到这里导航资源文件就创建成功了)

现在我们先去修改 MainActivity 和创建几个 Fragment 用来实现测试布局。

我觉得,是时候该好好聊聊 Android Navigation 了。-天真的小窝

我这里一共创建了两个 Fragment 作为页面,并且每个页面给了一个提示文本代表这是不同的页面。上面就是页面布局以及代码文件。

HomeFragment(主页)

UserFragment(用户页面)

我觉得,是时候该好好聊聊 Android Navigation 了。-天真的小窝

创建好 Fragment 之后打开 nav_graph.xml 文件将刚创建的两个 Fragment 添加进去,可视化操作的话直接点击上面的小图标,将 HomeFragment 和 UserFragment 依次添加进去就好了。当然也可以手写 fragment 节点到 navigation 节点下。

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/homeFragment">

    <fragment
        android:id="@+id/homeFragment"
        android:name="com.pbkhub.smartcity.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" />

    <fragment
        android:id="@+id/userFragment"
        android:name="com.pbkhub.smartcity.UserFragment"
        android:label="fragment_user"
        tools:layout="@layout/fragment_user" />

</navigation>

fragment 节点的属性解析:

android:id 可以看作是一个唯一的路由 ID,跳转页面时会用到,通过 R.id.xxxx 方式引用

android:name 对应实现的 fragment 对象

android:label 描述标签

tools:layout fragment 布局文件

我觉得,是时候该好好聊聊 Android Navigation 了。-天真的小窝

之后我们有新的页面就加入到这个文件中就好了(nav_graph.xml 中的导航不止可以用 fragment 还能添加 Activity)。

navigation 的 app:startDestination 属性可以指定默认启动的导航

接下来修改 MainActivity 的布局文件,我使用 FragmentContainerView 作为 Fragment 加载容器,在页面底部添加了两个按钮用来实现使用 navigation 跳转不同的页面。

我觉得,是时候该好好聊聊 Android Navigation 了。-天真的小窝
<?xml version="1.0" encoding="utf-8"?>
<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:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/a_main_nav_host"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center">

        <Button
            android:id="@+id/a_main_btn_openhome"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="打开主页" />

        <Button
            android:id="@+id/a_main_btn_openuser"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="30dp"
            android:text="用户页面" />

    </LinearLayout>


</LinearLayout>

到这里,我们的布局基本搞定。接下来就是实现点击按钮切换不同页面了。

通过调用 getSupportFragmentManager().findFragmentById() 方法传如入 FragmentContainerView 的 View id 获取 NavHostFragment 对象,调用 NavHostFragment 对象的 getNavController() 方法就能获取 NavController 对象。

然后调用 initView() 方法初始化组件以及给底部的 Button 绑定点击事件,调用 NavController 对象的 navigate() 方法传入在 nav_graph.xml 中定义 fragment 节点的 id 就能切换到指定的 fragment 了。具体的实现可以参考下面的代码

我觉得,是时候该好好聊聊 Android Navigation 了。-天真的小窝
package com.pbkhub.smartcity;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentContainerView;
import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    FragmentContainerView mNavHost;
    Button mOpenHome, mOpenUser;

    NavHostFragment navHostFragment;
    NavController navController;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.a_main_nav_host);
        navController = navHostFragment.getNavController();


        initView();
    }

    private void initView() {
        mNavHost = findViewById(R.id.a_main_nav_host);
        mOpenHome = findViewById(R.id.a_main_btn_openhome);
        mOpenUser = findViewById(R.id.a_main_btn_openuser);

        mOpenUser.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                navController.navigate(R.id.userFragment);
            }
        });
        mOpenHome.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                navController.navigate(R.id.homeFragment);
            }
        });

    }

}

好了,今天关于 Navigation 简单使用的博客就先写到这里吧,如果不鸽的话我还会写一下 Navigation + BottomNavigationView 的使用。

上面的示例代码放到 GitHub 了,如果有需要看看的可以自取:https://github.com/PBK-B/android-use-navigation-demo

如果遇到了问题欢迎到我的博客 https://bin.zmide.com/?p=967 给我留言,每条留言都会回的哦 😋。