Navigation
navigation是jetpack组件库的一个组件,用来控制application内容的切换。
我的开发环境
Android Studio 3.4.1,
gradle 5.1.1
Androidx
老板本可能会有一系列的让你升级等日志。
核心概念
- Navigation graph: 导航图,一个XML资源,它包含集中在一个位置上的所有与导航相关的信息。这包括所有单独的内容区域(destination),以及用户可以通过应用程序访问的可能路径。就类似一个流程图,如下图。
- NavHost:显示导航图中的destination的空容器。导航组件包含一个默认的NavHost实现NavHostFragment,
- NavController: 控制NavHost 容器内容的切换。
- destination:目的片段,一般是Fragment(由于google推荐单Activity模式app) ,也可以是Activity.如下图的三个界面
- action:动作,就相当于intent,表示从一个destination到另一个destination,如下图的箭头。
基本使用
一 添加依赖
注意: I如果你想在Android Studio中使用导航,你必须使用 Android Studio 3.3或更高版本.
def nav_version = "2.1.0-alpha05"
implementation "androidx.navigation:navigation-fragment:$nav_version" // For Kotlin use navigation-fragment-ktx
implementation "androidx.navigation:navigation-ui:$nav_version" // For Kotlin use navigation-ui-ktx
二 添加导航图(Navigation graph)
在“项目”窗口中,右键单击该
res
目录,然后选择“ New > Android Resource File。出现“ New Resource File对话框。在File name字段中键入名称,例如“nav_graph”。
-
在资源类型下拉列表中选择 Navigation,然后单击“ 确定”。
如图
添加成功后会在**res\navigation\ **下生成一个 nav_graph.xml文件
三 添加NavHost
NavHost是一个空的容器,用来切换destination。类似于用framelayout来切换fragment的东西。默认实现是NavHostFragment
在activity_mani.xml中添加NavHostFragment
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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=".MainActivity">
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
android:name
属性包含NavHost实现的类名。app:navGraph
属性将NavHostFragment与导航图关联起来。导航图指定用户可以导航到的NavHostFragment中 的所有目的地。app:defaultNavHost="true"
属性确保您的NavHostFragment拦截系统后退按钮。默认值只能是一个NavHost。如果同一布局中有多个主机(例如,两个窗格布局),请确保只指定一个默认NavHost。
另外也可以通过拖动控件来添加NavHostFragment,如下图
在拖动过去添加之后,会弹出一个包含导航图列表的对话框,选择你要关联的导航图就行了。
ps: activity中引入NaNavHoseFragment 固定宽高 真机正常,在模拟器中不显示,,,,,有大佬知道往告知。
四 创建 destination
打开步骤二的 nav_graph.xml,如下图。
- 用来增加dectination,可直接创建新的destination(一般为Fragment)或者选择已经创建好的destination
- 列出您的导航主机和图形编辑器中当前的所有目的地(destination)。
- 包含导航图的可视化表示。您可以在设计视图和文本视图中的底层XML表示之间切换。
- 显示导航图中当前选定项的属性。
点击上面红色方框的图标
Create new destination 可以创建 destination(一般都是Fragment 或者Activity),placeholder是个空的 destination,占位用的。下面的列表是所有存在的destination,你可以手动编写类继承Fragment,它同样会作为destination存在该列表中。
默认第一个创建的destination为启动页, 我们也可以选中destination单击右键来设置。
也可以同<navigation>元素下通过以下属性来指定。
app:startDestination="@id/blankFragment"
五 跳转
添加Action
选中destination 长按右边的圆圈拖动连接你的下一个destination。
当然也可以在xml里手写。
<?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/blankFragment">
<fragment android:id="@+id/blankFragment" android:name="com.hutlon.navigationdemo.BlankFragment"
android:label="fragment_blank" tools:layout="@layout/fragment_blank">
<action android:id="@+id/action_blankFragment_to_blankFragment22" app:destination="@id/blankFragment2"/>
</fragment>
<fragment android:id="@+id/blankFragment2" android:name="com.hutlon.navigationdemo.BlankFragment2"
android:label="fragment_blank_fragment2" tools:layout="@layout/fragment_blank_fragment2"/>
</navigation>
NavController处理Action跳转
跳转需要通过NavController对象的navigate(int id)方法来跳转
NavController对象获取的三种方式
NavHostFragment.findNavController(Fragment)
Navigation.findNavController(Activity, @IdRes int viewId)
Navigation.findNavController(View)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Navigation.findNavController(view)
button.setOnClickListener {
//id就是nav_graph导航图里面的action id,就和startActivty一样
Navigation.findNavController(view).navigate(R.id.action_blankFragment_to_secondeFragment)
}
}
有跳转肯定就有返回
navigateUp()
方法就是返回
跳转动画
点击箭头,右边属性栏有个Animations列表。默认都是null,我这设置官方自带的进出栈动画。
Enter 进入一个目的地 (例如 A跳B。 B显示时执行的动画)
Exit 退出一个目的地 (例如 A跳B。 A隐藏时执行的动画)
Pop Enter通过 pop 操作进入目的地 (例如 A跳B。 B navigateUp 后 A再次出现时执行的动画)
Pop Exit 通过pop 操作退出目的地 (例如 A跳B。 B navigateUp 后 B消失时执行的是这个动画)
修改之后的nav_graph.xml的代码如下:
<action android:id="@+id/action_blankFragment_to_secondeFragment" app:destination="@id/secondeFragment"
app:launchSingleTop="false"
app:popUpToInclusive="false" app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:enterAnim="@anim/nav_default_enter_anim" app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"/>
传递数据
例如 A到B
在导航图设计页面,单机destination B ,在右边的属性栏添加Argument,弹出如下窗口
Name:属性名称
Type:属性类型
Array:是否是数组,无论Nullable是否选中,数组都是可以为null的,并且默认值也只为null
Nullable:是否可为空
-
Default Value:默认值
Type支持的类型
创建成功后的xml如下:增加了个argument标签
<fragment android:id="@+id/secondeFragment" android:name="com.hutlon.navigationdemo.SecondFragment"
android:label="fragment_seconde" tools:layout="@layout/fragment_second">
<action android:id="@+id/action_secondeFragment_to_threeFragment" app:destination="@id/threeFragment"/>
<argument android:name="name" app:argType="string" android:defaultValue="no"/>
</fragment>
在A中传递数据
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Navigation.findNavController(view)
val bundle =Bundle()
bundle.putString("name","lee");
button.setOnClickListener {
//id就是nav_graph导航图里面的action id,就和startActivty一样
Navigation.findNavController(view).navigate(R.id.action_blankFragment_to_secondeFragment,bundle)
}
}
B中接收数据
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var name = arguments?.get("name")
Log.d("aaa", name as String?)
}
如果A中没有传递值,则B中的name使用默认值。
安全的参数传递 Safe Args
在项目的 gradle文件中的dependencies添加类路径
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.1.0-alpha05"
在model的gradle文件中添加引用
只针对kotlin的
apply plugin: "androidx.navigation.safeargs.kotlin"
java 或则 java混编 的
apply plugin: "androidx.navigation.safeargs"
当重新编译以后,会为作为发送的destination和接受destination以及操作的action 生成对应的类
会为每个起始destination创建一个类,名称是 destination名称+Directions,
如 BlankFragment 生成的类为 BlankFragmentDirections每个action创建一个对应的类,如Action名称为confirmAction,则类名为ConfirmAtion.
会为每个接受的destination 创建一个类,类名是 destination名称+Args,如SecondFragment生成的类为SecondFragmentArgs
起始destination
fun safeMode(){
val action = BlankFragmentDirections.actionBlankFragmentToSecondeFragment("hello")
Navigation.findNavController(view!!).navigate(action)
}
接受destination
var name = SecondFragmentArgs.fromBundle(arguments!!).name
其实我一开始没明白安全在哪里,后来可能如下(有更靠谱说法欢迎纠正)
两种方式的发送数据如下,
bundle.putString("name","lee")//普通方式
val action = BlankFragmentDirections.actionBlankFragmentToSecondeFragment("hello")
接受数据代码如下
var name = arguments?.getString("name")//普通方式
var name1 = SecondFragmentArgs.fromBundle(arguments!!).name
对比一下,很明显普通方式无论是发送还是接受的时候都需要指定类型,这样就可能存在类型不匹配的情况,如传的是int,获取的时候却是取string。