Android之Navigation学习(一)Navigation初体验
一、前言
前段时间,博主再一次学习了Fragment的使用。在熟悉项目模块的时候,发现了项目模块中运用较多的就是Fragment,比如说登录页面之间的跳转,于是就看到了Navigation——这是一种便捷管理Fragment的工具。 在学习之前,贴出一下谷歌给出的codelab的教学地址,在下面的地址,你可以学习到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.
Navigation,中文译为导航,顾名思义,从起始地指向目的地,而在app中可以理解为不同内容的跳转,而有个Android基础的小伙伴一定了解,在一个Activity中进行不同内容的展示靠的组件就是Fragment,即Fragment之间的跳转。最典型的就是导航栏,如下图
在介绍具体使用之前,先要给大家说明下Navigation涉及到的三大组成:
- 导航图:在一个集中位置包含所有导航相关信息的 XML 资源。这包括应用内所有单个内容区域(称为目标)以及用户可以通过应用获取的可能路径。
NavHost
:显示导航图中目标的空白容器。导航组件包含一个默认NavHost
实现 (NavHostFragment
),可显示 Fragment 目标。NavController
:在NavHost
中管理应用导航的对象。当用户在整个应用中移动时,NavController
会安排NavHost
中目标内容的交换。
上述是官网教程的解释,下面是博主具体解读下三个组成的具体成分。
-
Navigation Graph
这一部分包含的是整个导航图的xml文件,必须放在
res
的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类。 -
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
-
Navigation Controller
这一部分就是真正管理
Navigation
的模块,其作用就是操作Fragment之间的一系列行为,包括fragment
跳转,action
加过渡动画。
三、Navigation操作
下面以codelabs示例进行说明。
1. Navigation布局
Step 1 在navigation文件夹下生成“navigation”文件。
然后组织Fragment之间的关系,设定初始Fragment。(下图中带有home标志的即为Navigation初始Fragment)
Step 2 在activity.xml
写入fragment
组件,声明NavHostFragment
与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
在navigation.xml
的fragment
中携带注册的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
的范围限定为导航图,以在图表的目标之间共享与界面相关的数据。