七日打卡——Android学习之Navigation学习(一)Navigation初体验

492 阅读4分钟

Android之Navigation学习(一)Navigation初体验

一、前言

前段时间,博主再一次学习了Fragment的使用。在熟悉项目模块的时候,发现了项目模块中运用较多的就是Fragment,比如说登录页面之间的跳转,于是就看到了Navigation——这是一种便捷管理Fragment的工具。 在学习之前,贴出一下谷歌给出的codelab的教学地址,在下面的地址,你可以学习到Navigation组件的使用:

Jetpack Navigation

二、Navigation简介

Navigation refers to the interactions that allow users to navigate across, into, and back out from the different pieces of content within your app.

from:developer.android.com/guide/navig…

Navigation,中文译为导航,顾名思义,从起始地指向目的地,而在app中可以理解为不同内容的跳转,而有个Android基础的小伙伴一定了解,在一个Activity中进行不同内容的展示靠的组件就是Fragment,即Fragment之间的跳转。最典型的就是导航栏,如下图

头条导航栏

在介绍具体使用之前,先要给大家说明下Navigation涉及到的三大组成:

  • 导航图:在一个集中位置包含所有导航相关信息的 XML 资源。这包括应用内所有单个内容区域(称为目标)以及用户可以通过应用获取的可能路径。
  • NavHost:显示导航图中目标的空白容器。导航组件包含一个默认 NavHost 实现 (NavHostFragment),可显示 Fragment 目标。
  • NavController:在 NavHost 中管理应用导航的对象。当用户在整个应用中移动时,NavController 会安排 NavHost 中目标内容的交换。

上述是官网教程的解释,下面是博主具体解读下三个组成的具体成分。

  1. Navigation Graph

    这一部分包含的是整个导航图的xml文件,必须放在resnavigation文件夹下。

    navigation文件夹

    其主要作用为布局Fragment,指示Fragment之间的导向(destination):也就是所谓Navigation的XML内容——fragment+Fragment之间的跳转action+必要时的数据传输argument。xml示例如下:

    <!--xxxx_navigation-->
    <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"
        app:startDestination="@id/xxxx_fragment">
      <fragment
            android:id="@+id/home_dest"
            android:name="xxxxxxxxxx.xxxxFragment"
            tools:layout="@layout/xxxx_fragment">
          <argument
                android:name="flowStepNumber"
                app:argType="integer"
                android:defaultValue="1"/>
            <action
                android:id="@+id/next_action"
                app:destination="@+id/yyyy_fragment"
                app:enterAnim="@anim/slide_in_right"
                app:exitAnim="@anim/slide_out_left"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right" />
        </fragment>
      ......
    </navigation>
    

    app:startDestination="@id/home_dest"用来指明哪一个Fragment作为起始显示的内容。

    action则包含着Fragment之间的跳转动作,指明跳转目的地以及跳转动画。

    当然起始Fragment(StartDestination)和action也可以在代码中动态增加,在Navigation Graph在代码中体现为NavGraph类。

  2. Navigation Host

    这一部分是导航图的容器,其主要作用是在activity的xml文件中提供一个存放导航图的容器,通常就是一个Fragment,其声明需如下:

    Fragment的name表明这一Fragment是导航所需要用的容器;

    app:navGraph则需要指明Navigation Graph的名称。

      <!-- XXX_Activity.xml -->   
    	<fragment
                android:id="@+id/my_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/xxxx_navigation" />
    

    在代码中则需要使用NavHostFragment来获取到这一Fragment,进而再通过获取Navigation Controller来操作Fragment之间的跳转等行为。

    val host: NavHostFragment = supportFragmentManager
                    .findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment? ?: return
    
  3. Navigation Controller

    这一部分就是真正管理Navigation的模块,其作用就是操作Fragment之间的一系列行为,包括fragment跳转,action

加过渡动画。

anim文件夹

三、Navigation操作

下面以codelabs示例进行说明。

1. Navigation布局

Step 1 在navigation文件夹下生成“navigation”文件。

navigation生成

然后组织Fragment之间的关系,设定初始Fragment。(下图中带有home标志的即为Navigation初始Fragment)

navigation具体图

Step 2 在activity.xml写入fragment组件,声明NavHostFragmentNavGraph

navGraph

当然我们还可以动态设定初始Fragment与Graph。

NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.my_nav_host_fragment);  // Hostfragment
if (navHostFragment != null) {
  navController = navHostFragment.getNavController();
  NavInflater navInflater = navController.getNavInflater();
  NavGraph navGraph = navInflater.inflate(R.navigation.my_nav_graph);
}
navGraph.setStartDestination(R.id.xxx_fragment);
navInflater.setGraph(navGraph);

2. Navigation执行action

2.1 触发action,实现Fragment的跳转

val options = navOptions {
    anim {//动画放在anim文件夹下
        enter = R.anim.slide_in_right
        exit = R.anim.slide_out_left
        popEnter = R.anim.slide_in_left
        popExit = R.anim.slide_out_right
    }
}
view.findViewById<Button>(R.id.xxxxx).setOnClickListener {
  findNavController().navigate(R.id.xxxx_destination, args, options)
}

view.findViewById<Button>(R.id.xxxxx).setOnClickListener(
  findNavController().createNavigateOnClickListener(R.id.xxxx_destination, args)
)

findNavController()即寻找到当前Navigation的NavController,通过两种方式进行导航跳转navigate()createNavigationClickListener(),然后传入目的地参数(在xml文件中的action所指示的destination

2.2 Fragment携带argument进行跳转

首先要在navigation

携带argument

navigation.xmlfragment中携带注册的argument会被加载到navArgs()中.

action可以根据目前所在的fragment生成相应的XXXActions(如HomeFragmentActions)

class FlowStepFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        setHasOptionsMenu(true)

//        val flowStepNumber = arguments?.getInt("flowStepNumber")

        val safeArgs: FlowStepFragmentArgs by navArgs()
        val flowStepNumber = safeArgs.flowStepNumber

        return when (flowStepNumber) {
            2 -> inflater.inflate(R.layout.flow_step_two_fragment, container, false)
            else -> inflater.inflate(R.layout.flow_step_one_fragment, container, false)
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
		······
    }
}

class HomeFragment : Fragment() {
  ······  
  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
				······
 
        view.findViewById<Button>(R.id.navigate_action_button)?.setOnClickListener {
            val flowStepNumberArg = 1
            val action = HomeFragmentDirections.nextAction(flowStepNumberArg)
            findNavController().navigate(action,options)
        }
    }
	······
}

四、小结

上述即为Navigation使用的小小分享,使用Navigation对于Fragment的管理有着很大的作用。

除此之外,拿一下Google的介绍来说明一下Navigation的作用吧。

导航组件提供各种其他优势,包括以下内容:

  • 处理 Fragment 事务。
  • 默认情况下,正确处理往返操作。
  • 为动画和转换提供标准化资源。
  • 实现和处理深层链接。
  • 包括导航界面模式(例如抽屉式导航栏和底部导航),用户只需完成极少的额外工作。
  • Safe Args - 可在目标之间导航和传递数据时提供类型安全的 Gradle 插件。
  • ViewModel 支持 - 您可以将 ViewModel 的范围限定为导航图,以在图表的目标之间共享与界面相关的数据。