Jetpack之Navigation

333 阅读3分钟

这里选用kotlin的项目,首先引入navigation和kotlin app/build.gradle如下

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: "androidx.navigation.safeargs.kotlin"
android {
...

    dataBinding {
        enabled true
    }
    compileOptions {
        sourceCompatibility = '1.8'
        targetCompatibility = '1.8'
    }
    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    //kotlin
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
    implementation 'androidx.core:core-ktx:1.2.0-alpha02'

    implementation 'androidx.appcompat:appcompat:1.1.0-rc01'

    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    //navigation
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_dep"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_dep"
}
project/build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext.kotlin_version = '1.3.41'
    ext.nav_dep = '2.1.0-beta02'
    ext.coroutinesVersion = '1.2.1'
    repositories {
        google()
        jcenter()

    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.4.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_dep"
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()

    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

如果采用kotlin的话,项目中可能报错 Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 。这时候只需在build的gradle中添加如下即可

compileOptions {
    sourceCompatibility = '1.8'
    targetCompatibility = '1.8'
}
kotlinOptions {
    jvmTarget = JavaVersion.VERSION_1_8
}

现在的项目中只有一个默认创建的MainActivity以及XML,首先在res目录上右键new-AndroidResourceFile,在弹出的菜单中的ResourceType中选择navigation,然后填入fileName确定,然后在res/navigation目录下就会生成一个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"
            xmlns:tools="http://schemas.android.com/tools" >
</navigation>

然后切换到Design模式中

其中1为导航主页及所有导航路径,2为可视化的导航路径图,3为选中的导航路径中的item的属性。然后我们单击2中的添加destination按钮,选择create new destination

可以看到直接出来的创建fragment的页面,按照这个步骤创建两个fragment A和B,创建完成之后在2中区域就可以看到两个fragment了,如果要想从AFragment跳转到BFragment,那么直接在该面板选中A,并且拖拽到B,最终的效果如图

这时候可以看到区域1中的Host仍然为空,那么我们就添加这个Host,这个是系统提供的fragment,打开activity_main.xml,在design面板的palette中搜索NavHost,结果如下

将这个NAVHostFragment拖拽到xml中,代码如下

<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

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

</androidx.constraintlayout.widget.ConstraintLayout>

这里的defaultNavHost就是制定当前导航路径的主Fragment,而navGraph则是指定当前主fragment下所包含的所有导航信息。接下来看nav_graph这个文件的详细信息

<?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/AFragment">

    <fragment
            android:id="@+id/AFragment"
            android:name="com.example.jetpacknavigation.AFragment"
            android:label="fragment_a"
            tools:layout="@layout/fragment_a">
        <action android:id="@+id/action_AFragment_to_BFragment"
                app:destination="@id/BFragment"
        />
    </fragment>
    
    <fragment android:id="@+id/BFragment"
              android:name="com.example.jetpacknavigation.BFragment"
              android:label="fragment_b"
              tools:layout="@layout/fragment_b">
    </fragment>
</navigation>

这里面的action就是刚才添加箭头的作用,从自动生成的名字也可以看出就是从A到B的跳转。其中在根节点中的startDestination则为初始化默认显示的页面,而下面的action中的destination则是要到达的页面。那么如何 跳转呢,在AFragment中的按钮点击事件设置为

Navigation.findNavController(it).navigate(R.id.action_AFragment_to_BFragment)

就是拿到这个NavController然后调用它的navigate方法,参数则是上面定义的action的id,这样就可以实现跳转。很多时候跳转的时候需要携带参数,这时候我们可以通过如下方式传参

var bundle=Bundle()
bundle.putString("test","hello")
Navigation.findNavController(it).navigate(R.id.action_AFragment_to_BFragment,bundle)

在BFragment中通过getArguments.getString可以获取到传递过来的值。当然系统还为我们提供了一种简单的方法,直接在xml中生命参数

<fragment android:id="@+id/BFragment"
          android:name="com.example.jetpacknavigation.BFragment"
          android:label="fragment_b"
          tools:layout="@layout/fragment_b">
    <argument
            android:name="taskId"
            app:argType="string"/>
</fragment>

这里直接声明了该fragment需要一个String类型的参数,在A中调用Navigate方法如下

这里的xxxFragmentDirections是自动生成的类,在BFragment中只需加入

private val args:BFragmentArgs by navArgs()

这里的args就会解析到getArgument的值。不仅仅是传值,还可以设置动画,这里选中箭头,然后在右侧的Animation中选择相应的动画即可,这里有系统默认的,也可以在res/anim目录下建自己想要的动画