jetpack组件---导航框架:navigation

2021-11-16  本文已影响0人  会写代码的小猿猴

什么是navigation?

谷歌官方:
Navigation 是一个框架,用于在 Android 应用中的“目标”之间导航,该框架提供一致的 API,无论目标是作为 Fragment、Activity 还是其他组件实现。
navigation就是一套起初主要用于管理fragment之间跳转的框架,后面逐渐也被大家应用到activity间的跳转,说到activity之间的跳转,方式已经很多了,比如最原始的Intent跳转,然后到比较好用的Arouter,特别是Arouter,保证了每个activity的独立性,有一定的解耦功能,我觉得比使用navigation来管理activity跳转更好用,所以我们可以人为navigation就是用来管理fragment的跳转的吧。

使用前的准备

如需添加 Navigation 的依赖项,您必须将 Google Maven 代码库添加到项目中。如需了解详情,请参阅 Google 的 Maven 代码库

在应用或模块的 build.gradle 文件中添加所需工件的依赖项:

dependencies {
  val 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:")
}

上面是谷歌给出的声明依赖项的标准,总的需要添加那么多依赖,当然如果你是和我一样使用kotlin开发的,可以选择性添加,比如我的是这样的:

 implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    def nav_version = "1.0.0-alpha05"
    implementation "android.arch.navigation:navigation-fragment:$nav_version" 
    implementation "android.arch.navigation:navigation-ui:$nav_version"
    androidTestImplementation "android.arch.navigation:navigation-testing:$nav_version"

接下来就是创建资源文件,创建方法:在 res 目录右击,选择 New > Android Resource File,Resource type 选择 Navigation。如下图:


image.png
image.png

可能你在创建的过程中会出现Resource type 找不到navigation选项的问题,那是因为在 Android Studio 3.2 Canary 14 以上的版本中,需要打开 Preferences -> Experimental -> Enable Navigation Editor ,然后重启Android studio

一些其他的准备

如需将 Safe Args 添加到您的项目,请在顶层 build.gradle 文件中包含以下 classpath

buildscript {
    repositories {
        google()
    }
    dependencies {
        val nav_version = "2.3.5"
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
    }
}

您还必须应用以下两个可用插件之一。

如需生成适用于 Java 模块或 Java 和 Kotlin 混合模块的 Java 语言代码,请将以下行添加到应用或模块的 build.gradle 文件中:

plugins {
    id("androidx.navigation.safeargs")
}

此外,如需生成适用于仅 Kotlin 模块的 Kotlin 语言代码,请添加以下行:

plugins {
    id("androidx.navigation.safeargs.kotlin")
}

上面这些其他的准备是官方给出的,说的非常模糊,那具体是干什么的呢?其实就是安全性传递参数需要做的准备,后面说到参数传递的时候会再次提到。

Navigation初使用

我们先把navigation用起来,比如现在我们先实现一个activity里面包含两个fragment,让这两个fragment之间可以相互跳转。

我们先创建一个新的项目navigationDemo;
然后创建第一个fragment:

class FirstFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.first_fragment, container, false)
    }
}

first_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="這是第一個fragment"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="15dp"
        android:text="跳轉"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

接着继续创建第二个fragment:

class SecondFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_nav_second_fragment, container, false)
    }
}

second_fragment.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".SecondFragment">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="这是第二个fragment"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/backFirst"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="15dp"
        android:text="回去"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

这两个fragment创建好以后,我们就可以做些navigation的准备工作了,也就是上文所提到的准备工作:
创建资源文件,创建方法:在 res 目录右击,选择 New > Android Resource File,Resource type 选择 Navigation。取名nav_graph

它会在res下的navigation下生成一个nav_graph.xml的文件,文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/nav_graph"
   >
</navigation>

这个文件里面放的是navigation的导航图,我们在里面添加创建的第一个fragment:

<?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/nav_first_fragment"
   >
    <fragment
        android:id="@+id/nav_first_fragment"
        android:name="com.example.navicationdemo.FirstFragment"
        android:label="first"
        tools:layout="@layout/first_fragment">
    </fragment>
    
</navigation>

参数说明:
android:id 任意命名,在fragment之间创建action时需要用到
android:name 关联的fragment类全名,这里是我们创建的第一个fragment的类全名
android:label 辨识标签,无实际意义,可随意命名
tools:layout 指定该fragment相应的布局文件
app:startDestination 指定第一个fragment

接下来一样的将第二个fragment也添加进去:

<?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"
   >
    <fragment
        android:id="@+id/nav_first_fragment"
        android:name="com.example.navicationdemo.FirstFragment"
        android:label="first"
        tools:layout="@layout/first_fragment">
    </fragment>
    <fragment
        android:id="@+id/nav_second_fragment"
        android:name="com.example.navicationdemo.SecondFragment"
        android:label="second"
        tools:layout="@layout/second_fragment">
    </fragment>
</navigation>

这样我们就已经往导航视图里面添加了两个fragment,导航图右边显示如下:


image.png

可以看到我们只是单纯地将他添加进去,两者之间并无关联,我们的初衷是希望实现他们之间点击按钮分别跳转,所以需要在nav_graph.xml中两个fragment内部分别添加action

<?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"
   >
    <fragment
        android:id="@+id/nav_first_fragment"
        android:name="com.example.navicationdemo.FirstFragment"
        android:label="first"
        tools:layout="@layout/first_fragment">
        <action
            android:id="@+id/firstfragment_to_secondfragment"
            app:destination="@+id/nav_second_fragment" />
        >
    </fragment>
    <fragment
        android:id="@+id/nav_second_fragment"
        android:name="com.example.navicationdemo.SecondFragment"
        android:label="second"
        tools:layout="@layout/second_fragment">
        <action
            android:id="@+id/secondfragment_to_firstfragment"
            app:destination="@+id/nav_first_fragment" />
    </fragment>
</navigation>

action里面的属性意义:
android:id 该action的id,做跳转操作指定action时使用它作为标识
app:destination 指定该action执行时需要跳转到的fragment的id

然后右边两个fragment之间也有了联系:


image.png

我们看到有两个箭头分别指向对方,这就是跳转的action。

下面就是在fragment里面实现按钮点击事件
FirstFragment:

class FirstFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.first_fragment, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        button1.setOnClickListener {
            Navigation.findNavController(getView()!!)
                .navigate(R.id.firstfragment_to_secondfragment)
        }
    }
}

跳转时调用 NavController 的 navigate 方法执行跳转,navigate 的参数可以是一个 fragment 在导航图 nav_graph 中的 id,也可以是 action 的 id。我们这里使用的是action的id。

SecondFragment也是一样的方法:

class SecondFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.second_fragment, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        backFirst.setOnClickListener { 
            Navigation.findNavController(getView()!!)
                .navigate(R.id.secondfragment_to_firstfragment)
        }
    }
}

这样咱们的基本跳转就实现了,可以实现跳转,但在实际中为了让跳转更加明显,通常需要加入跳转时的动画效果。跳转动画应该在action里面实现,你可以在action里面使用代码实现,也可以使用design直接实现,
我们为了快捷就用第二种方法,具体做法:切换到nav_graph.xml,点击右上角Design,点击视图上的箭头(其实箭头就是代表action)


image.png

右边四种动画效果选择如下:


image.png

这样就实现了跳转的动画效果。

最后把它放到activity里面,这里有两种实现方式,第一种是直接在activity的xml中应用:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">

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

</androidx.constraintlayout.widget.ConstraintLayout>

第二种方式是通过代码创建 NavHostFragment:
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 
    ... >

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/frame_layout" />
</android.support.constraint.ConstraintLayout>

MainActivity:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_navigation)
    
    val finalHost = NavHostFragment.create(R.navigation.nav_graph)
    supportFragmentManager.beginTransaction()
            .replace(R.id.frame_layout, finalHost)
            .setPrimaryNavigationFragment(finalHost) // 等价于 xml 中的 app:defaultNavHost="true"
            .commit()
}

最后运行项目,点击按钮就可以实现跳转了。

数据的传递

比如第一个fragment跳转第二个fragment时携带参数,先在nav_graph.xml的第二个fragment里添加argument

<fragment
        android:id="@+id/nav_second_fragment"
        android:name="com.example.navicationdemo.SecondFragment"
        android:label="second"
        tools:layout="@layout/second_fragment">
        <argument
            android:name="name"
            android:defaultValue="zzp" />
        <action
            android:id="@+id/secondfragment_to_firstfragment"
            app:destination="@+id/nav_first_fragment"
            app:enterAnim="@anim/nav_default_enter_anim"
            app:exitAnim="@anim/nav_default_exit_anim"
            app:popEnterAnim="@anim/nav_default_pop_enter_anim"
            app:popExitAnim="@anim/nav_default_pop_exit_anim" />
    </fragment>

参数说明:argument的name类似于键值对里的key值,defaultvalue就是接收参数失败时的数据。
FirstFragment 添加数据

 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        button1.setOnClickListener {
            val bundle= bundleOf("name" to "路飞")
            Navigation.findNavController(getView()!!)
                .navigate(R.id.firstfragment_to_secondfragment,bundle)
        }
    }

SecondFargment

 override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        Log.d("zzp","你好 ${arguments?.getString("name")}")
        return inflater.inflate(R.layout.second_fragment, container, false)
    }

运行效果:


image.png

参看文档:
Navigation 详解一 (大神写的,写的很牛,建议大家去看看他的这篇文章)
谷歌官方简陋说明
Navigation官方API

上一篇 下一篇

猜你喜欢

热点阅读