当前位置:   article > 正文

Android Jetpack架构组件 — Navigation入坑详解

android jetpack 组件

本文首发于微信公众号「Android开发之旅」,欢迎关注 ,获取更多技术干货

前言

Navigation 直接翻译即为导航,它是 Android Jetpack 组件之一,让单 Activity 应用成为首选架构。应用内Fragment页面的跳转则由 Navigation 来处理,开发者无需在处理 FragmentTransaction 的复杂性以及相关的转场动画。

具体使用

在app的gradle.build中添加依赖:

  1. def nav_version = "2.1.0"
  2. implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
  3. implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

首先我们定义三个Fragment,分别为Fragment1,Fragment2和Fragment3。实现逻辑为Fragment1点击跳转到Fragment2,Fragment2点击跳转到Fragment3,Fragment3跳转到Fragment1同时点击返回键时也返回到Fragment1。

navigation: 导航视图XML的根结点。里面定义相关fragment的跳转逻辑。

首先需要在res资源目录下新建 navigation 文件夹,右键新建一个Navigation resource file命名为nav_graph_main.xml。

文件左下脚分为两个Tab:Design和Text。Design视图是可视化的,可以直接选择相关fragment。Text视图是我们手写相关配置。

我们看下定义的nav_graph_main.xml文件:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <navigation xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. app:startDestination="@id/fragment1">
  6. <fragment
  7. android:id="@+id/fragment1"
  8. android:name="com.jetpack.jetpackdemo.navigation.fragment.Fragment1"
  9. android:label="Fragment1"
  10. tools:layout="@layout/fragment1_layout">
  11. <action
  12. android:id="@+id/fragment1_action"
  13. app:destination="@+id/fragment2" />
  14. </fragment>
  15. <fragment
  16. android:id="@+id/fragment2"
  17. android:name="com.jetpack.jetpackdemo.navigation.fragment.Fragment2"
  18. android:label="Fragment2"
  19. tools:layout="@layout/fragment2_layout">
  20. <action
  21. android:id="@+id/fragment2_action"
  22. app:destination="@+id/fragment3" />
  23. </fragment>
  24. <fragment
  25. android:id="@+id/fragment3"
  26. android:name="com.jetpack.jetpackdemo.navigation.fragment.Fragment3"
  27. android:label="Fragment3"
  28. tools:layout="@layout/fragment3_layout">
  29. <action
  30. android:id="@+id/fragment3_action"
  31. app:popUpTo="@id/fragment1" />
  32. </fragment>
  33. </navigation>

navigation根节点中有个startDestination字段,他表示的是默认展示的是哪一个页面。通过fragment标签来定义要路由的相关页面。id为fragment唯一标识。name为包名,必须保证正确。layout为fragment的布局文件,配置后方便在Design视图中查看。

fragment中配置了子节点 action 。action表示的就是具体要路由的行为。同样id也是其唯一标识,destination表示的是目的地,即需要路由到具体的某一个页面。popUpTo表示弹出到某一个页面。action还有其他的属性比如配置动画等,具体请看Demo。

NavHostFragment是导航视图的展示容器,
  1. <fragment
  2. android:id="@+id/nav_host_fragment"
  3. android:name="androidx.navigation.fragment.NavHostFragment"
  4. android:layout_width="0dp"
  5. android:layout_height="0dp"
  6. app:defaultNavHost="true"
  7. app:layout_constraintBottom_toBottomOf="parent"
  8. app:layout_constraintLeft_toLeftOf="parent"
  9. app:layout_constraintRight_toRightOf="parent"
  10. app:layout_constraintTop_toTopOf="parent"
  11. app:navGraph="@navigation/nav_graph_main" />

name为固定写法,必须指明为

androidx.navigation.fragment.NavHostFragment

defaultNavHost字段表示是否拦截返回按键操作。

若为true,需要的Activity中重写onSupportNavigateUp方法。
因为默认情况下返回键是不会回退fragment页面的。

  1. override fun onSupportNavigateUp(): Boolean {
  2. return findNavController(R.id.nav_host_fragment).navigateUp()
  3. }

navGraph字段即为我们配置的navigation导航视图。

NavController

通过findNavController来获取NavController,通过controller的navigate或者navigateUp进行页面之间的路由操作。

那么在三个页面的点击按钮的逻辑就是挑战相应的页面:

  1. mBtn.setOnClickListener {
  2. Navigation.findNavController(it).navigate(R.id.fragment1_action)
  3. }

通过指定action的id来告诉Navigation跳转的逻辑。其他页面也是一样。

最终效果:

我们来总结下 navigation、NavHostFragment以及NavController之间的关系。

navigation就是规划了很多的路线,而这些路线需要在NavHostFragment中才能进行展示。展示后这么多的路线该怎么走呢,决定权就在NavController手中了,就像是方向盘一样,控制着该走哪一个路线。

传递参数

在上文中我们讲解了navigation相关的知识,其中还有一个子标签:argument。是用来定义参数的。比如我们在fragment2标签中添加argument标签如下:

  1. <argument
  2. android:name="name"
  3. android:defaultValue="navigation导航"
  4. app:argType="string"
  5. app:nullable="false" />

那么在fragment1跳转到fragment2的时候就可以携带参数了。其中 name 表示参数名称。defaultValue即为默认值。argType为参数的类型。nullable表示是否可以为空。

fragment之间传递参数有两种方式:

  • 传统的Bundle方式
  • 通过谷歌提供的safeArgs
传统的Bundle方式

通过Bundle来设置和获取参数。

在fragment1中进行设置:

  1. mBtn.setOnClickListener {
  2. //如果要使用 xml中argument的默认值则直接new Bundle() 传入即可
  3. val args = Bundle()
  4. args.putString("name","通过bundle传递参数")
  5. Navigation.findNavController(it).navigate(R.id.fragment1_action, args)
  6. }

在fragment2中进行获取参数:

  1. val args = arguments
  2. val name = args?.getString("name")
  3. mTvName.text = name

这样就可以将参数进行传递了。上面这种方式大家有没有觉得有什么问题呢?

参数名称 “name” 我们在三处进行的手动填写。这样会很容易导致拼写错误以及修改的时候容易漏改。很不友好。所以谷歌给我们提供了一个插件:safeArgs。下面我们来看下具体使用。

safeArgs

首先需要进行配置,在项目的 build.gradle 中添加classpath配置:

  1. dependencies {
  2. classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0'
  3. }

再在app的 build.gradle添加 apply plugin。

apply plugin: 'androidx.navigation.safeargs'

项目重新构建后会知道为fragment生成后缀为 Directions的文件。并为navigation中有 argument 标签的fragment自动生成后缀为Args的文件。

通过后缀为 Directions的文件进行参数的设置。后缀为Args的文件进行参数的获取。

fragment1中进行设置:

  1. mBtn.setOnClickListener {
  2. val args = Fragment1Directions.fragment1Action().setName("通过safeArgs进行参数传递")
  3. Navigation.findNavController(it).navigate(R.id.fragment1_action, args.arguments)
  4. }

fragment2中进行获取:

  1. val name = Fragment2Args.fromBundle(arguments!!).name
  2. mTvName.text = name

这样就完成了fragment之间参数的传递。完全避免了手动设置参数的逻辑。直接通过setter和getter进行参数的操作。

总结

总体来说Navigation的使用并不复杂,它让我们单Activity架构成为可能,无需关心具体的fragment的跳转逻辑。但是同样也是有问题的,通过源码分析我们知道
在NavHostFragment的onCreateView中是创建了FrameLayout,也就是说其实真正的容器是FrameLayout。在创建FragmentNavigator的时候内部使用的是replace这个API,而不是show和hide。这就会导致fragment每次生命周期都会重新执行。所以和ViewModel结合使用效果应该更好。

扫描下方二维码关注公众号,获取更多技术干货。

作者:李四爷

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/266978?site
推荐阅读
相关标签
  

闽ICP备14008679号