Android Navigation 详解
Posted Eli Shaw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Navigation 详解相关的知识,希望对你有一定的参考价值。
一、导航概述
Navigation 用于 Fragment 的管理。他可以让 Fragrant 之间的切换,拥有像 Activity 间一样的跳转。与 DrawerLayout(抽屉式布局)、ActionBar(导航栏)等有简洁完美的对接。
二、开发环境设置
注意:Navigation 需要在 android Studio 3.3 或更高版本中才可使用。(并且在 androidx 中支持的更好,之前没有使用 androidx 一直无法添加 Fragment 目的地)
若要在项目中包含导航支持,请将以下依赖项添加到应用程序的 build.gradle 文件中:
dependencies
def nav_version = "2.1.0-rc01"
// Java
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"
// Kotlin
implementation "androidx.navigation:navigation-fragment-ktx:$nav_dep"
implementation "androidx.navigation:navigation-ui-ktx:$nav_dep"
三、创建 navigation graph (导航图)
导航图是一个 XML 资源文件,在导航图中有两个概念:
1.Destination:导航图中的每一个导航被称为 Destination (目的地)。
2.Action:导航与导航之间使用 Action (事件) 连接,用于说明两个导航之间的跳转关系。
若要向项目添加导航图,请执行以下操作:
在项目的 res 目录下,新建一个 navigation 文件夹,右键 navigation 文件夹,依次选择:New > Navigation resource file
填写文件名称,如:nav_graph,点击 OK。
四、编辑导航图
在编辑导航图前,需要先创建 Activity,与相关的 Fragment。(此处使用 1 个 Activity,三个 Fragment)
/**
* @Author: Eli Shaw
* @Date: 2019-08-18 10:10:42
* @Description: Activity 容器类
*/
class NavigationActivity : AppCompatActivity()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_navigation)
/**
* @Author: Eli Shaw
* @Date: 2019-08-18 10:07:13
* @Description: 登录页
*/
class LoginFragment : Fragment(), View.OnClickListener
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View?
// Inflate the layout for this fragment
val viewRoot = inflater.inflate(R.layout.fragment_login, container, false)
return viewRoot
/**
* @Author: Eli Shaw
* @Date: 2019-08-18 10:08:52
* @Description: 注册页
*/
class RegisterFragment : Fragment(), View.OnClickListener
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View?
// Inflate the layout for this fragment
val viewRoot = inflater.inflate(R.layout.fragment_register, container, false)
return viewRoot
/**
* @Author: Eli Shaw
* @Date: 2019-08-18 10:40:42
* @Description: 首页
*/
class HomeFragment : Fragment()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View?
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false)
创建好 nav_graph 导航图后,双击进行编辑(与常规布局文件很相似)
1、指定 Activity 容器
在 Activity 布局文件中指定导航图的容器
<LinearLayout
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=".navigation.NavigationActivity">
<fragment
android:id="@+id/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/nav_graph"/>
</LinearLayout>
android:name:指定 Fragment 的类型为 NavHostFragment。
app:defaultNavHost="true":让 Navigation 容器处理返回事件,在 Navigation 容器中如果有页面的跳转,点击返回按钮会先处理 容器中 Fragment 页面间的返回,处理完容器中的页面,再处理 Activity 页面的返回。如果值为 false 则直接处理 Activity 页面的返回。
app:navGraph:指定 Navigation 文件。
2、添加 Action (事件)
拖动选中的(目的地)右方的圆点到另一个(目的地)即可创建一个 Action(事件)。
点击左下角的 Text ,将显示如下代码
<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/loginFragment">
<!--登录 Fragment 包含两个事件:一个是注册页,一个是首页 -->
<fragment android:id="@+id/loginFragment" android:name="cn.eli.jetpack.navigation.LoginFragment"
android:label="fragment_login" tools:layout="@layout/fragment_login">
<action android:id="@+id/actionLoginToRegister" app:destination="@id/registerFragment"/>
<action android:id="@+id/actionLoginToHome" app:destination="@id/homeFragment"/>
</fragment>
<!--注册 Fragment 包含一个事件:返回到登录页-->
<fragment android:id="@+id/registerFragment" android:name="cn.eli.jetpack.navigation.RegisterFragment"
android:label="fragment_register" tools:layout="@layout/fragment_register">
<action android:id="@+id/actionRegisterToLogin" app:destination="@id/loginFragment"/>
</fragment>
<!--首页 Fragment 没有事件-->
<fragment android:id="@+id/homeFragment" android:name="cn.eli.jetpack.navigation.HomeFragment"
android:label="fragment_home" tools:layout="@layout/fragment_home"/>
</navigation>
3、导航图的复用
<navigation> 是导航图的根元素,Destination (目的地) 与 Action (事件) 都是 <navigation> 元素中的节点。
<navigation> 中可以包含 <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" android:id="@+id/nav_multiplex"
app:startDestination="@id/findPwdFragment">
<fragment android:id="@+id/findPwdFragment" android:name="cn.eli.jetpack.navigation.FindPwdFragment"
android:label="fragment_find_pwd" tools:layout="@layout/fragment_find_pwd">
<action android:id="@+id/action_findPwdFragment_to_nav_graph" app:destination="@id/nav_graph"/>
</fragment>
<!--引入另一个导航图-->
<include app:graph="@navigation/nav_graph"/>
</navigation>
五、导航图间的跳转
1、获取 NavController 的方式
导航图使用 NavController 控制(目的地)之间的跳转,在 Activity,Fragment 中获取 NavController 有以下 6 种方式:
1.Fragment.findNavController()
2.View.findNavController()
3.Activity.findNavController(viewId: Int) //只有 Activity 中可以使用
4.NavHostFragment.findNavController(Fragment)
5.Navigation.findNavController(Activity, @IdRes int viewId) //只有 Activity 中可以使用
6.Navigation.findNavController(View)
2、导航中目的地间的传值
导航中目的地间也可以使用 Bundle 进行传值
//使用 bundle 传值
val bundle = Bundle()
bundle.putInt(ARG_NAV, navController)
findNavController().navigate(R.id.actionLoginToRegister, bundle)
下面是登录页、注册页完整代码
private const val ARG_NAV = "nav"
/**
* @Author: Eli Shaw
* @Date: 2019-08-18 10:07:13
* @Description: 登录页
*/
class LoginFragment : Fragment(), View.OnClickListener
private var navController: Int = 0
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
arguments?.let
navController = it.getInt(ARG_NAV)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View?
// Inflate the layout for this fragment
val viewRoot = inflater.inflate(R.layout.fragment_login, container, false)
//注册点击事件
viewRoot.findViewById<Button>(R.id.btnRegister).setOnClickListener(this)
//登录点击事件
viewRoot.findViewById<Button>(R.id.btnLogin).setOnClickListener
findNavController().navigate(R.id.actionLoginToHome)
return viewRoot
/**
* 注册点击事件
*/
override fun onClick(view: View)
if (++navController > 4)
navController = 1
//使用 bundle 传值
val bundle = Bundle()
bundle.putInt(ARG_NAV, navController)
//根据 navController 值的不同,使用不同的获取 NavController 的方式
when (navController)
//Fragment.findNavController()
1 -> findNavController().navigate(R.id.actionLoginToRegister, bundle)
//View.findNavController()
2 -> view.findNavController().navigate(R.id.actionLoginToRegister, bundle)
//NavHostFragment.findNavController(Fragment)
3 -> NavHostFragment.findNavController(this).navigate(R.id.actionLoginToRegister, bundle)
//Navigation.findNavController(View)
4 -> Navigation.findNavController(view).navigate(R.id.actionLoginToRegister, bundle)
private const val ARG_NAV = "nav"
/**
* @Author: Eli Shaw
* @Date: 2019-08-18 10:08:52
* @Description: 注册页
*/
class RegisterFragment : Fragment(), View.OnClickListener
private var navController: Int = 0
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
arguments?.let
navController = it.getInt(ARG_NAV)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View?
// Inflate the layout for this fragment
val viewRoot = inflater.inflate(R.layout.fragment_register, container, false)
viewRoot.findViewById<Button>(R.id.btnBackLogin).setOnClickListener(this)
return viewRoot
override fun onClick(view: View)
val bundle = Bundle()
bundle.putInt(ARG_NAV, navController)
view.findNavController().navigate(R.id.actionRegisterToLogin, bundle)
3、导航中目的地间的转场动画
自定义转场动画
cut_to_enter.xml (入场动画)
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:toYDelta="0"
android:fromYDelta="100%"
android:duration="1500"/>
</set>
cut_to_exit.xml(出场动画)
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="1500"
android:fromYDelta="0"
android:toYDelta="-100%"/>
</set>
在导航图中编辑
以上是关于Android Navigation 详解的主要内容,如果未能解决你的问题,请参考以下文章
Android Jetpack Navigation:如何在 OnNavigatedListener 中获取目的地的片段实例?
「Android高级开发」10年老程序员经验谈:navigation入门详解
「Android高级开发」10年老程序员经验谈:navigation入门详解