Jetpack源码解析----Navigation入门(一)
以下内容翻译自android官网
Jetpack源码解析----Navigation入门(一)
概览
Navigation 是指支持用户导航、进入和退出应用中不同内容片段的交互。Android Jetpack 的Navigation 组件可帮助您实现导航,无论是简单的按钮点击,还是应用栏和抽屉式导航栏等更为复杂的模式,该组件均可应对。导航组件还通过遵循一套既定原则来确保一致且可预测的用户体验。
组成
Navigation 组件由以下三个关键部分组成:
- Navigation graph:在一个集中位置包含所有导航相关信息的 XML 资源。这包括应用内所有单个内容区域(称为目标)以及用户可以通过应用获取的可能路径。
- NavHost:显示 Navigation graph 中目标的空白容器。Navigation 组件包含一个默认 NavHost 实现 (NavHostFragment),可显示 Fragment 目标。
- NavController:在 NavHost 中管理应用导航的对象。当用户在整个应用中移动时,NavController 会安排 NavHost 中目标内容的交换。
在应用中导航时,您告诉 NavController,您想沿导航图中的特定路径导航至特定目标,或直接导航至特定目标。NavController 便会在 NavHost 中显示相应目标。
优势
导航组件提供各种其他优势,包括以下内容:
- 处理 Fragment 事务。
- 默认情况下,正确处理往返操作。
- 为动画和转换提供标准化资源。
- 实现和处理深层链接。
- 包括导航界面模式(例如抽屉式导航栏和底部导航),用户只需完成极少的额外工作。
- Safe Args - 可在目标之间导航和传递数据时提供类型安全的 Gradle 插件。
- ViewModel 支持 - 您可以将 ViewModel 的范围限定为导航图,以在图表的目标之间共享与界面相关的数据。
此外,您还可以使用 Android Studio 的 Navigation Editor
来查看和编辑导航图。 详情参考下面 Navigation 组件使用入门 及其截图
注意:如果要使用导航和 Android Studio,则必须使用 Android Studio 3.3 或更高版本
。
Safe Args
使用安全Args传递安全类型的数据
Navigation组件具有一个称为Safe Args的Gradle插件,该插件可生成简单的对象和构建器类以进行类型安全的导航并访问任何关联的参数。强烈建议使用Safe Args导航和传递数据,因为它可以确保类型安全。
在某些情况下,例如,如果您不使用Gradle,则不能使用Safe Args插件。在这些情况下,可以使用Bundle直接传递数据。
要将 Safe Args添加 到您的项目中,请在*build.gradle文件中添加以下内容:
buildscript {
repositories {
google()
}
dependencies {
def nav_version = "2.3.2"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}
}
您还必须应用两个可用插件之一。
要生成适用于Java或Java和Kotlin混合模块的Java语言代码,请将此行添加到您的 app
or module
的 build.gradle文件中:
apply plugin: "androidx.navigation.safeargs"
另外,要生成适用于仅Kotlin模块的Kotlin代码,请添加:
apply plugin: "androidx.navigation.safeargs.kotlin"
启用Safe Args后,您生成的代码将为每个操作以及每个发送和接收目标包含以下类型安全的类和方法。
- 将为操作起源的每个destination 创建一个类。此类的名称是原始目的地的名称,后跟单词“
Directions
”。例如,如果原始目的地是一个名为的片段SpecifyAmountFragment
,则将生成的类SpecifyAmountFragmentDirections
。
此类为始发目标中定义的每个操作都有一个方法。 - 对于用于传递参数的每个 action ,都会创建一个内部类,该内部类的名称基于该 action 。例如,如果调用
confirmationAction
,该操作,则该类名为ConfirmationAction
。如果您的操作包含不带的参数defaultValue,则可以使用关联的操作类来设置参数的值。 - 为接收destination创建一个类。此类的名称是目的地的名称,后跟单词“
Args
”。例如,如果将目标片段命名为,ConfirmationFragment
,则生成的类称为ConfirmationFragmentArgs
。使用此类的fromBundle()
方法检索参数。
下面的示例显示如何使用这些方法设置参数并将其传递给navigate() 方法:
@Override
public void onClick(View view) {
EditText amountTv = (EditText) getView().findViewById(R.id.editTextAmount);
int amount = Integer.parseInt(amountTv.getText().toString());
ConfirmationAction action =
SpecifyAmountFragmentDirections.confirmationAction()
action.setAmount(amount)
Navigation.findNavController(view).navigate(action);
}
在接收目标的代码中,使用 getArguments()方法检索bundle 并使用其内容。使用-ktx 依赖项时,Kotlin用户还可以使用by navArgs()属性委托来访问参数。
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
TextView tv = view.findViewById(R.id.textViewAmount);
int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount();
tv.setText(amount + "")
}
通过global action使用安全Args
将Safe Args与global action
配合使用时 ,必须为android:id
根<navigation>
元素提供一个值,如以下示例所示:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_nav"
app:startDestination="@id/mainFragment">
...
</navigation>
Navigation 基于android:id
值为<navigation>
元素生成Directions类
。 例如,如果您的<navigation>
元素带有android:aaa@qq.com+id/main_nav
,则生成的类称为MainNavDirections
。 <navigation>
元素内的所有destinations 均已生成了使用与上一节中所述相同的方法访问所有关联的全局操作的方法。
Navigation 组件使用入门
设置您的环境
注意:Navigation 组件需要 Android Studio 3.3 或更高版本
,并且依赖于 Java 8 语言功能
。
如需在您的项目中添加 Navigation 支持,请向应用的 build.gradle 文件添加以下依赖项:
dependencies {
def nav_version = "2.3.2"
// Java language implementation
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"
// Kotlin
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
// Feature module Support
implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"
// Testing Navigation
androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
// Jetpack Compose Integration
implementation "androidx.navigation:navigation-compose:1.0.0-alpha03"
}
如需了解如何向您的项目添加其他架构组件,请参阅向项目添加组件。
创建navigation graph
导航发生在应用中的各个目的地(即您的应用中用户可以导航到的任意位置)之间。这些目的地是通过actions连接的。
navigation graph 是一种资源文件,其中包含您的所有destinations 和 actions。该 graph 会显示应用的所有导航路径。
图 1 直观显示了一个示例应用的导航图,该应用包含 6 个目的地(通过 5 个操作连接)。每个目的地均由一个预览缩略图表示,连接操作由箭头表示,该箭头展示用户可以如何从一个目的地导航到另一个目的地。
图1. 一个navigation graph,显示了由 5 个操作连接的 6 个不同目的地的预览。
- Destinations 是指应用中的不同内容区域。
- Actions 是指目的地之间的逻辑连接,表示用户可以采取的路径。
如需向项目添加 navigation graph,请执行以下操作:
- 在“Project”窗口中,右键点击 res 目录,然后依次选择 New > Android Resource File。此时系统会显示 New Resource File 对话框。
- 在 File name 字段中输入名称,例如“nav_graph”。
- 从 Resource type 下拉列表中选择 Navigation,然后点击 OK。
完成上面三步之后如果你没有添加 navigation 引用,会提示你,点击ok即可自动添加。
创建完成后如下:
当您添加首个导航图时,Android Studio 会在 res 目录内
创建一个 navigation 资源目录
。该目录包含您的导航图资源文件(例如 nav_graph.xml
)。
注意:向您的项目添加导航图时,如果您尚未将导航依赖项添加到应用的 build.gradle 文件中,Android Studio 会显示一条提示,并为您提供添加依赖项的选项。但请注意,Android Studio 3.4 添加了非 KTX 1.0.0 版本的依赖项,因此,如果您使用的是 Kotlin 或打算使用 2.0.0 或更高版本,请务必替换这些值。参考上面截图
Navigation Editor
添加图表后,Android Studio 会在 Navigation Editor
中打开该图表。在 Navigation Editor 中,您可以直观地修改导航图,或直接修改底层 XML。
图2. Navigation Editor
- Destinations panel:列出了导航宿主和目前位于 Graph Editor 中的所有目的地。
- Graph Editor:包含导航图的视觉表示形式。您可以在 Design 视图和 Text 视图中的底层 XML 表示形式之间切换。
- Attributes:显示导航图中当前所选项的属性。
上面三部分分别对应下面截图中的123
点击 Text
标签页可查看相应的 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"
android:id="@+id/nav_graph">
</navigation>
<navigation>
元素是导航图的根元素。当您向图表添加目的地和连接操作时,可以看到相应的 <destination>
和 <action>
元素在此处显示为子元素。如果您有嵌套图表,它们将显示为子 <navigation>
元素。
向 Activity 添加 NavHost
navigation host 是 Navigation 组件的核心部分之一。navigation host 是一个空容器,用户在您的应用中导航时,destinations 会在该容器中交换进出
。
navigation host 必须派生于 NavHost。Navigation 组件的默认 NavHost 实现 (NavHostFragment) 负责处理 Fragment 目的地的交换
。
注意:Navigation 组件旨在用于具有一个主 Activity 和多个 Fragment 目的地的应用。主 Activity 与导航图相关联,且包含一个负责根据需要交换目的地的 NavHostFragment。在具有多个 Activity 目的地的应用中,每个 Activity 均拥有其自己的导航图。
通过 XML 添加 NavHostFragment
以下 XML 示例显示了作为应用主 Activity 一部分的 NavHostFragment:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.appcompat.widget.Toolbar
.../>
<androidx.fragment.app.FragmentContainerView
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" />
<com.google.android.material.bottomnavigation.BottomNavigationView
.../>
</androidx.constraintlayout.widget.ConstraintLayout>
请注意以下几点:
-
android:name 属性包含
NavHost 实现类的类名称
。 -
app:navGraph 属性
将 NavHostFragment 与导航图相关联
。导航图会在此 NavHostFragment 中指定用户可以导航到的所有目的地
。 -
app:defaultNavHost=“true” 属性
确保您的 NavHostFragment 会拦截系统返回按钮
。请注意,只能有一个默认 NavHost。如果同一布局
(例如,双窗格布局)中有多个hosts,请务必仅指定一个默认 NavHost
。
使用 Layout Editor 添加 NavHostFragment
您也可以使用 Layout Editor 向 Activity 添加 NavHostFragment,具体操作步骤如下:
- 在项目文件列表中,双击 Activity 的布局 XML 文件,以在 Layout Editor 中将其打开。
- 在 Palette 窗格内,选择
Containers
类别,或者搜索“NavHostFragment
”。 - 将 NavHostFragment 视图拖动到您的 Activity 上。
- 接下来,在随即显示的 Navigation Graphs 对话框中,选择需要与此 NavHostFragment 相关联的相应导航图,然后点击 OK。
添加后源码如下:
<fragment
android:id="@+id/fragment3"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="409dp"
android:layout_height="729dp"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph"
tools:layout_editor_absoluteX="1dp"
tools:layout_editor_absoluteY="1dp" />
向 navigation graph 添加destinations
您可以从现有的 Fragment 或 Activity 创建目的地。您还可以使用 Navigation Editor 创建新目的地,或创建占位符以便稍后替换为 Fragment 或 Activity。
在本示例中,我们来创建一个新目的地。如需使用 Navigation Editor 添加新目的地,请执行以下操作:
- 在 Navigation Editor 中,点击 New Destination 图标 ,然后点击 Create new destination。
- 在随即显示的 New Android Component 对话框中,创建您的 Fragment。如需详细了解 Fragment,请参阅 Fragment 文档。
当您返回到 Navigation Editor 中时,会发现 Android Studio 已将此目的地添加到图中。
图 3 显示了目的地和占位符目的地的示例。
图3. 目的地和占位符
如需了解向导航图添加目的地的其他方式,请参阅创建destination。
destination详解
点击一个destination以将其选中,并注意 Attributes 面板中显示的以下属性:
- Type 字段指示在您的源代码中,该目的地是作为 Fragment、Activity 还是其他自定义类实现的。
- Label 字段包含该目的地的 XML 布局文件的名称。
- ID 字段包含该目的地的 ID,它用于在代码中引用该目的地。
- Class 下拉列表显示与该目的地相关联的类的名称。您可以点击此下拉列表,将相关联的类更改为其他目的地类型。
点击 Text 标签页可查看导航图的 XML 视图。XML 中同样包含该目的地的 id、name、label 和 layout 属性,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
app:startDestination="@id/blankFragment">
<fragment
android:id="@+id/blankFragment"
android:name="com.example.cashdog.cashdog.BlankFragment"
android:label="Blank"
tools:layout="@layout/fragment_blank" />
</navigation>
将某个屏幕指定为start destination
start destination 是用户打开您的应用时看到的第一个屏幕,也是用户退出您的应用时看到的最后一个屏幕
。Navigation Editor 使用房子图标 表示起始目的地。
所有目的地就绪后,您便可以选择起始目的地,具体操作步骤如下:
- 在 Design 标签页中,点击相应目的地,使其突出显示。
- 点击 Assign start destination 按钮 。或者,您可以右键点击该目的地,然后点击 Set as Start Destination。
点击 Assign start destination 按钮截图
点击 Set as Start Destination 截图
最后结果如下:
连接destinations
action是指目的地之间的逻辑连接。action 在导航图中以箭头表示。action 通常会将一个目的地连接到另一个目的地,不过您也可以创建 global action,此类 action 可让您从应用中的任意位置转到特定目的地
。
借助 actions,您可以表示用户在您的应用中导航时可以采取的不同路径。请注意,如需实际导航到各个目的地,您仍然需要编写代码以执行导航操作。如需了解详情,请参阅本主题后面的导航到目的地部分。
您可以使用 Navigation Editor 将两个目的地连接起来,具体操作步骤如下:
-
在 Design 标签页中,将鼠标悬停在目的地的右侧,该目的地为您希望用户从中导航出来的目的地。该目的地右侧上方会显示一个圆圈,如图 4 所示。
图4. 一个包含操作连接圆圈的目的地 -
点击您希望用户导航到的目的地,并将光标拖动到该目的地的上方,然后松开。这两个目的地之间生成的线条表示操作,如图 5 所示。
图5. 通过操作连接目的地 -
点击箭头以突出显示该操作。此时 Attributes 面板中会显示以下属性:
- Type 字段包含“Action”。
- ID 字段包含该操作的 ID。
- Destination 字段包含目的地 Fragment 或 Activity 的 ID。
-
点击 Text 标签,以切换到 XML 视图。现在,一个 action 元素已添加到源目的地中。该操作有一个 ID 和一个目的地属性(其中包含下一个目的地的 ID),如以下示例所示:
<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" app:startDestination="@id/blankFragment"> <fragment android:id="@+id/blankFragment" android:name="com.example.cashdog.cashdog.BlankFragment" android:label="fragment_blank" tools:layout="@layout/fragment_blank" > <action android:id="@+id/action_blankFragment_to_blankFragment2" app:destination="@id/blankFragment2" /> </fragment> <fragment android:id="@+id/blankFragment2" android:name="com.example.cashdog.cashdog.BlankFragment2" android:label="fragment_blank_fragment2" tools:layout="@layout/fragment_blank_fragment2" /> </navigation>
在导航图中,操作由 <action>
元素表示。action 至少应包含自己的 ID 和用户应转到的目的地的 ID。
注意上面连接destination,选中点后不要松开,直到成功创建action
还可以像下面这样创建
action 属性截图如下:
导航到目的地
导航到目的地是使用 NavController 完成的,后者是一个在 NavHost 中管理应用导航的对象。每个 NavHost 均有自己的相应 NavController。您可以使用以下方法之一检索 NavController:
Kotlin:
- Fragment.findNavController()
- View.findNavController()
- Activity.findNavController(viewId: Int)
Java:
- NavHostFragment.findNavController(Fragment)
- Navigation.findNavController(Activity, @IdRes int viewId)
- Navigation.findNavController(View)
使用 FragmentContainerView 创建 NavHostFragment,或通过 FragmentTransaction 手动将 NavHostFragment 添加到您的 Activity 时,尝试通过 Navigation.findNavController(Activity, @IdRes int) 检索 Activity 的 onCreate() 中的 NavController 将
失败。您应改为直接从 NavHostFragment 检索 NavController。
NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
NavController navController = navHostFragment.getNavController();
使用 Safe Args 确保类型安全
如需在目的地之间导航,建议使用 Safe Args Gradle 插件。该插件可以生成简单的对象和构建器类,这些类支持在目的地之间进行类型安全的导航和参数传递
。
注意:如需了解其他导航方式,请参阅上面的导航到目的地。
如需将 Safe Args
添加到您的项目,请在顶层 build.gradle 文件中包含以下内容 :
buildscript {
repositories {
google()
}
dependencies {
def nav_version = "2.3.1"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}
}
您还必须应用以下两个可用插件之一。
如需生成适用于 Java 模块或 Java 和 Kotlin 混合模块的 Java 语言代码,请将以下行添加到应用或模块的 build.gradle 文件中:
apply plugin: "androidx.navigation.safeargs"
此外,如需生成适用于仅 Kotlin 模块的 Kotlin 语言代码,请添加以下行:
apply plugin: "androidx.navigation.safeargs.kotlin"
根据迁移到 AndroidX 文档,您的 gradle.properties 文件中必须具有 android.useAndroidX=true。
启用 Safe Args 后,该插件会生成代码,其中包含您定义的每个action的类和方法。对于每个action,Safe Args 还会为每个源destination(生成相应操作的目的地)生成一个类
。生成的类的名称由源destination类的名称和“Directions”一词组成
。例如,如果目的地的名称为 SpecifyAmountFragment,生成的类的名称为 SpecifyAmountFragmentDirections。生成的类为源destination中定义的每个操作提供了一个静态方法。该方法会将任何定义的操作参数作为参数,并返回可传递到 navigate() 的 NavDirections 对象
。
例如,假设我们的导航图包含一个操作,该操作将源目的地 SpecifyAmountFragment 和接收目的地 ConfirmationFragment 连接起来。
Safe Args 会生成一个 SpecifyAmountFragmentDirections 类,其中只包含一个 actionSpecifyAmountFragmentToConfirmationFragment() 方法(该方法会返回 NavDirections 对象)。然后,您可以将返回的 NavDirections 对象直接传递到 navigate(),如以下示例所示:
@Override
public void onClick(View view) {
NavDirections action =
SpecifyAmountFragmentDirections.actionSpecifyAmountFragmentToConfirmationFragment();
Navigation.findNavController(view).navigate(action);
}
如需详细了解如何使用 Safe Args 在目的地之间传递数据,请参阅使用 Safe Args 传递类型安全的数据。
推荐阅读
-
Laravel5 源码解析 (一)
-
微信跳一跳python辅助软件思路及图像识别源码解析
-
Zend Framework入门之环境配置及第一个Hello World示例(附demo源码下载)
-
基于Spring注解的上下文初始化过程源码解析(一)
-
netty源码解析(4.0)-28 ByteBuf内存池:PooledByteBufAllocator-把一切组装起来
-
Mybaits 源码解析 (十二)----- Mybatis的事务如何被Spring管理?Mybatis和Spring事务中用的Connection是同一个吗?
-
Tomcat源码分析三:Tomcat启动加载过程(一)的源码解析
-
Mybaits 源码解析 (十)----- 全网最详细,没有之一:Spring-Mybatis框架使用与源码解析
-
Mybaits 源码解析 (八)----- 全网最详细,没有之一:结果集 ResultSet 自动映射成实体类对象(上篇)
-
Mybaits 源码解析 (九)----- 全网最详细,没有之一:一级缓存和二级缓存源码分析