Navigation Of JetPack【译】

/ 默认分类Material Design / 没有评论 / 348浏览

原文地址: https://developer.android.google.cn/topic/libraries/architecture/navigation/

介绍

Jetpack是Android软件组件的集合,可以使你更轻松地开发出色的Android应用程序。这些组件可帮助你遵循最佳实践,免除编写样板代码并简化复杂任务,因此你可以专注于开发者更关系的业务代码。Jetpack包含androidx.*库中,与平台API分开。这意味着它提供向后兼容性并且比Android平台更频繁地更新,确保你始终可以访问最新和最好的Jetpack组件版本。 Navigation组件简化了Android应用程序中导航的实现。

Navigation原则

任何应用内导航的目标应该是为用户提供一致且可预测的体验。为了实现这一目标,Navigation架构组件可帮助你构建符合以下每个导航原则的应用程序。

应用具有固定的起点

应用应该具有固定起点,即用户从启动器启动应用时看到的界面。此起点也应该是用户在按下后退按钮后返回启动器时看到的最后一个界面。

应用可能存在第一次使用时的设置界面或者登陆界面,这种特殊性的界面不应该被视为应用的起点。

堆栈用来代表应用的“导航状态”

应用的导航状态应使用后进先出的结构表示。此“导航堆栈”中堆栈底部为应用程序的起始界面,而栈顶为当前界面。 改变此堆栈的操作必须全部集中在栈顶,要么“pushing”一个新的目标到栈顶,要们从栈顶“poping"一个目标出栈。

“向上”按钮永远不会退出应用

起点界面中不应该出现向上按钮。当应用是通过其他应用使用deeplink的方式启动时,向上按钮应该将用户带回上层界面而不是当时启动此应用的其他应用。

Up和Back在应用程序任务中是等效的

当系统返回键不会导致应用程序退出时,如在应用程序的任务栈中,当前用户不处于起点界面,这个时候系统返回键就不会退出应用。这种情况下呢,我们的Up按钮操作应该和系统返回键的操作效果相同。

DeepLink或者Navigate至相同界面生成相同的堆栈

用户可以在起始界面进入应用程序并导航到一个目标界面。如果可以的话,用户同样可以通过deeplink,跳转到相同的目标界面。上面这两种情况下,针对目标界面,我们应该产生相同的目标堆栈。说白了就是,无论他们如何到达目标界面,用户应该能够使用“Back”或“Up”按钮,都可以在目标界面导航回到起始界面。清除已有导航栈,取而代之的是deeplink的导航栈。

使用Navigation架构组件实现导航

Navigation架构组件简化了应用中destinations之间导航的实现。一组目标界面组成应用程序的navigation graph

目的地是你可以在应用中导航到的任何位置。虽然目标通常是代表特定屏幕的Fragment,但Navigation架构组件支持其他目标类型:

除目标之外,导航图还在称为actions的目标之间建立连接。图1显示了一个示例应用程序的导航图的直观表示,该应用程序包含由5个操作连接的6个目标。 alt

项目中构建Navigation

在创建导航图之前,必须为项目配置导航架构组件。要在Android Studio中设置项目,请执行以下步骤:

  1. 如果使用Beta,Release Candidate或Stable构建,则必须启用导航编辑器。点击File > Settings(Android Studio > Preferenceson Mac),在左侧菜单中选择Experimental,然后勾选Enable Navigation Editor并且重启Android Studio。
  2. 在应用或者模块的build.gradle中添加Navigation组件
dependencies {
    def nav_version = "1.0.0-alpha06"

    implementation "android.arch.navigation:navigation-fragment:$nav_version" // use -ktx for Kotlin
    implementation "android.arch.navigation:navigation-ui:$nav_version" // use -ktx for Kotlin

    // optional - Test helpers
    androidTestImplementation "android.arch.navigation:navigation-testing:$nav_version" // use -ktx for Kotlin
}
  1. 项目工程窗口,右键点击res目录并且选择New > Android Resource FileNew Resource File对话框出现。
  2. 在输入框中键入一个File name,如“nav_graph”。
  3. Resource type下拉框中选择Navigation
  4. 点击OK后,将进行下列操作
    • navigation目录将出现在res目录下
    • nav_graph.xml文件将被创建于navigation目录下
    • nav_graph.xml将被导航编辑器打开。这个xml文件中包含你的导航视图。
  5. 点击Text切换到xml文本视图。一个空的导航视图如下:
    <?xml version="1.0" encoding="utf-8"?>
    <navigation xmlns:android="http://schemas.android.com/apk/res/android">
    
``` 8. 点击**Design**切换到导航编辑视图。

看看导航编辑器

在导航编辑器中,你可以快速构建导航图,而不是手动构建图形的XML。如下图所示,导航编辑器有三个部分:

alt

导航编辑器的三部分如下:

  1. “目标”列表 - 列出“导航图编辑器”中当前的所有目标。
  2. 导航图编辑器 - 包含导航图的可视化表示。
  3. 属性编辑器 - 包含与导航图中的目标或操作关联的属性。

明确目的地

创建导航图的第一步是确定应用的目的地。你可以创建空白目标或从现有项目中的Fragment和Activity创建目标。 要确定应用的目标,请使用以下步骤:

  1. 在“导航图编辑器”中,单击New Destination出现New Destination对话框。
  2. 点击Create blank destination或者点击一个Fragment或者Activity。一个新的Android Component对话框将会出现
  3. 键入名称到Fragment Name,作为新建Fragment的类名。
  4. 键入名称到Fragment Layout Name,作为新建Fragment的布局名称。
  5. 点击Finish。表示目标的框显示在“导航图编辑器”和“目标”列表中。然后将进行下列内部操作:
    • 如果你创建了空白目标,则“导航图编辑器”会在目标中显示消息“Hello blank fragment”。如果单击Fragment或Activity,则“导航图编辑器”将显示该Activity或Fragment的布局预览。
    • 为项目创建Fragment子类。此类具有在步骤3中指定的名称
    • 为项目创建资源文件。此文件具有在步骤4中指定的名称 下图显示空白和现有目的地。 alt
  6. 单击新插入的目标以突出显示目标。 “属性”面板中显示以下属性:
    • Type字段包含“Fragment”或“Activity”,以指示目标是否在源代码中实现为Fragment或Activity
    • Label字段包含目标XML布局文件名
    • ID字段包含将用于在代码中引用目标的目标ID
    • Class字段包含目标类的名称
  7. 单击Text选项卡以切换到XML文本视图。 XML现在包含基于现有类和布局文件的名称的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="Blank"
        tools:layout="@layout/fragment_blank" />
</navigation>

目的地之间建立衔接

有多个目的地时才能建立连接。以下是包含两个空白目标的导航图的XML:

<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" />
    <fragment
        android:id="@+id/blankFragment2"
        android:name="com.example.cashdog.cashdog.BlankFragment2"
        android:label="Blank2"
        tools:layout="@layout/fragment_blank_fragment2" />
</navigation>

我们通过actions来连接目的地。如下步骤连接目的地:

  1. 在导航图编辑器中,将鼠标悬停在你希望用户导航的目标的右侧。目的地上会出现一个圆圈。 alt
  2. 单击并按住,将光标拖动到希望用户导航到的目标上,然后释放。绘制一条线以指示两个目的地之间的导航。 alt
  3. 点击箭头凸显Actions。如下属性将出现在属性界面:
    • Type字段显示“Action”
    • ID字段包含系统为操作分配的ID
    • Destination字段包含目标Fragment或Activity的ID
  4. 单击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>

将界面指定为起始目的地

导航图编辑器在应用程序的第一个目标名称旁边放置一个房屋图标。此图标表示这是导航图中的起始目标。你可以使用以下步骤将另一个目标指定为起始目标:

  1. 从导航图编辑器中,单击目标凸显;
  2. 单击“属性”面板中的Set Start Destination。选中目的地现在是起始目的地。

修改Activity以支持Navigation

Activity通过在布局中添加NavHost界面来托管应用程序的导航。 NavHost是一个空视图,当用户浏览应用程序时,目的地会被换入和换出。 在Navigation架构组件中的默认NavHost实现是NavHostFragment。 在布局中添加NavHost以后,我们需要使用navGraph属性来将我们的导航图和NavHostFragment联系起来。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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">

    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/my_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/nav_graph"
        app:defaultNavHost="true"
        />

</android.support.constraint.ConstraintLayout>

上面的例子中包含“app:defaultNavHost="true"属性,这个属性保证来NavHostFragment可拦截系统返回键的事件。你可以覆写AppCompatActivity.onSupportNavigateUp()方法并调用NavController.navigateUp,如下所示:

@Override
public boolean onSupportNavigateUp() {
    return Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp();
}

以编程方式创建NavHostFragment

你也可以使用NavHostFragment.create()以编程方式创建具有特定图形资源的NavHostFragment,如下例所示:

NavHostFragment finalHost = NavHostFragment.create(R.navigation.example_graph);
getSupportFragmentManager().beginTransaction()
    .replace(R.id.nav_host, finalHost)
    .setPrimaryNavigationFragment(finalHost) // this is the equivalent to app:defaultNavHost="true"
    .commit();

绑定目的地跳转到UI Widgets

使用NavController类导航到目标。可以使用以下静态方法之一拿到NavController:

拿到NavController以后,我们可以通过使用navigate()方法跳转到对应的目的地。navigate()方法接受一个resId作为参数。ID可以是导航图中特定目的地的ID或导航图中的 action ID。使用Action ID,相较于使用目的地ID有一些优势,如和导航相关的过渡效果。如下代码展示如何跳转到ViewTransactionsFragment:

viewTransactionsButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Navigation.findNavController(view).navigate(R.id.viewTransactionsAction);
    }
});

Android系统维护一个包含最后访问目的地的后栈。当用户打开应用程序时,应用程序的第一个目标位于堆栈中。每次调用navigate()方法都会将另一个目标放在堆栈顶部。相反,按向上或向后按钮分别调用NavController.navigateUp()和NavController.popBackStack()方法,以从堆栈中弹出顶部目标。 对于按钮,你还可以使用Navigation类的createNavigateOnClickListener()便捷方法导航到目标:

button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null));

将目标绑定到菜单驱动的UI组件

你可以通过在抽屉导航菜单或者是溢出菜单的xml中配置对应的目标ID来关联两者。以下代码段显示了详细信息界面目标,其ID为details_page_fragment

<fragment android:id="@+id/details_page_fragment"
     android:label="@string/details"
     android:name="com.example.android.myapp.DetailsFragment" />

目的地和菜单项使用同一个ID将会让两者自动关联。如下代码将展示如何让一个目的地和一个抽屉菜单关联(如menu_nav_drawer.xml)

<item
    android:id="@id/details_page_fragment"
    android:icon="@drawable/ic_details"
    android:title="@string/details" />

下面这段xml将展示如何将一个目的地和一个溢出菜单项关联(如menu_overflow.xml)

<item
    android:id="@id/details_page_fragment"
    android:icon="@drawable/ic_details"
    android:title="@string/details"
    android:menuCategory:"secondary" />

Navigation架构组件包含一个NavigationUI类。这个类中有几个静态方法可以用来关联菜单和目的地。例如,以下代码显示如何使用setupWithNavController()方法将抽屉菜单项连接到NavigationView。

NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
NavigationUI.setupWithNavController(navigationView, navController);

有必要使用这些NavigationUI方法设置菜单驱动的导航组件,以便这些UI元素的状态与NavController的更改保持同步。

在目的地之间传递数据

你可以通过两种方式在目标之间传递数据:使用Bundle对象或使用safe args Gradle插件以类型安全的方式传递数据。按照以下步骤使用Bundle对象在目标之间传递数据:

  1. 在导航图编辑器中,单击接收参数的目标位置。
  2. 在属性面板的Arguments一栏中点击Add (+),将会出现空名称和默认值字段。
  3. 双击名称并输入参数的名称
  4. 按Tab键切换到参数值文本框并输入参数的默认值
  5. 点击此目标左边的action 箭头,属性面板将会出现我们刚才添加的参数
  6. 点击Text切换到文本视图,对应的属性名称和默认值印入眼帘。
<fragment
   android:id="@+id/confirmationFragment"
   android:name="com.example.cashdog.cashdog.ConfirmationFragment"
   android:label="fragment_confirmation"
   tools:layout="@layout/fragment_confirmation">
   <argument android:name="amount" android:defaultValue=”0” />
  1. 在代码中,创建一个bundle对象并通过navigate()方法将其传递到目标
Bundle bundle = new Bundle();
bundle.putString("amount", amount);
Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);

接收方采用如下方式

TextView tv = view.findViewById(R.id.textViewAmount);
tv.setText(getArguments().getString("amount"));

添加侦听器以处理导航事件

你可以使用addOnNavigatedListener()方法将OnNavigatedListener添加到NavController。OnNavigatedListener在控制器导航到新目标时接收事件。你可以使用此处理程序进行特定于目标的更改,例如显示或隐藏某些UI元素。调用addOnNavigatedListener()时,如果当前目标存在,则立即将其发送给您的侦听器。

以类型安全的方式在目标之间传递数据

Navigation 架构组件有一个Gradle插件,称为safeargs,它生成简单的对象和构建器类,以便对目标和操作指定的参数进行类型安全访问。Safe args建立在Bundle方法的基础之上,但需要一些额外的代码来换取更多类型的安全性。如果你使用的是Gradle,则可以使用safe args插件。要添加此插件,请将“androidx.navigation.safeargs”插件添加到build.gradle。

apply plugin: 'com.android.application'
apply plugin: 'androidx.navigation.safeargs'

android {
   //...
}

配置Gradle插件后,请按照以下步骤使用类型安全的args:

  1. 在导航图编辑器中,单击接收参数的目标位置。
  2. 在属性面板的Arguments一栏中点击Add (+),将会出现空名称和默认值字段。
  3. 双击名称并输入参数的名称
  4. 按Tab键切换到参数类型下拉列表,选择参数类型
  5. 按Tab键切换到参数值文本框并输入参数的默认值
  6. 点击此目标左边的action 箭头,属性面板将会出现我们刚才添加的参数
  7. 点击Text切换到文本视图,对应的属性名称和默认值印入眼帘。
<fragment
    android:id="@+id/confirmationFragment"
    android:name="com.example.buybuddy.buybuddy.ConfirmationFragment"
    android:label="fragment_confirmation"
    tools:layout="@layout/fragment_confirmation">
    <argument android:name="amount" android:defaultValue="1" app:type="integer"/>
</fragment>

使用safeargs插件生成代码时,会为action以及发送和接收目标创建简单对象和构建器类。这些类是:

以下代码显示如何使用这些方法设置参数并将其传递给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并使用其内容。

@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 + "")
}

将目标分组为嵌套导航图

可以将一系列目的地分组为导航图中的子图。子图称为嵌套图,而包含图称为“根图”。嵌套图对于组织和重用应用程序UI的各个部分非常有用,例如单独的登录流程。与根图一样,嵌套图必须也有一个起始目标。嵌套图形封装了它的目的地;嵌套图外部的目标(例如根图上的目标)仅通过其起始目标访问嵌套图。下图显示了简单汇款应用程序的导航图。该图有两个流程:允许用户汇款的流程和允许用户查看其余额的流程。 alt

分组目的地为嵌套图,可进行如下步骤:

  1. 在“导航图编辑器”中,按住shift并单击要包含在嵌套图形中的目标。每个目的地都突出显示。
  2. 打开上下文菜单,然后选择Move to Nested Graph > New Graph。目标包含在嵌套图中。下图显示了Graph Editor中的嵌套图。 alt
  3. 单击嵌套图以突出显示它。 “属性”面板中显示以下属性:
    • Type字段为Nested Graph
    • ID字段包含嵌套图的系统分配ID。此ID用于引用代码中的嵌套图
  4. 双击嵌套图。将显示嵌套图中的目标。
  5. 在“目标”列表中,单击**“Root”**以返回到根导航图
  6. 点击Text切换到文本视图。一个嵌套导航图被加入到导航图中。此导航图具有自己的收尾呼应的navigation元素。此嵌套图的ID为sendMoneyGraph,startDestination属性指向嵌套图中的第一个目标(chooseRecipient)
<?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/mainFragment">
   <fragment
       android:id="@+id/mainFragment"
       android:name="com.example.cashdog.cashdog.MainFragment"
       android:label="fragment_main"
       tools:layout="@layout/fragment_main" >
       <action
           android:id="@+id/action_mainFragment_to_chooseRecipient"
           app:destination="@id/sendMoneyGraph" />
       <action
           android:id="@+id/action_mainFragment_to_viewBalanceFragment"
           app:destination="@id/viewBalanceFragment" />
   </fragment>
   <fragment
       android:id="@+id/viewBalanceFragment"
       android:name="com.example.cashdog.cashdog.ViewBalanceFragment"
       android:label="fragment_view_balance"
       tools:layout="@layout/fragment_view_balance" />
   <navigation android:id="@+id/sendMoneyGraph" app:startDestination="@id/chooseRecipient">
       <fragment
           android:id="@+id/chooseRecipient"
           android:name="com.example.cashdog.cashdog.ChooseRecipient"
           android:label="fragment_choose_recipient"
           tools:layout="@layout/fragment_choose_recipient">
           <action
               android:id="@+id/action_chooseRecipient_to_chooseAmountFragment"
               app:destination="@id/chooseAmountFragment" />
       </fragment>
       <fragment
           android:id="@+id/chooseAmountFragment"
           android:name="com.example.cashdog.cashdog.ChooseAmountFragment"
           android:label="fragment_choose_amount"
           tools:layout="@layout/fragment_choose_amount" />
   </navigation>
</navigation>
  1. 在代码中,将连接根图的操作的资源ID传递给嵌套图
Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);

使用引用其他导航图

<include app:graph="@navigation/included_graph"/>

为目标创建Deep Link

在Android中,deep link是指向应用中特定目标的URI。当你希望跳转到特定目的地以在应用中执行某项任务 时,这些URI非常有用,例如汇款流程,允许用户快速汇款给某人。

给目的地添加Deep Link

要在导航图中为目标添加Deep Link,请执行以下操作:

  1. 从导航图编辑器中,为deep link选择一个destination
  2. 单击“属性”面板的“Deep Link”部分中的+。将出现“Add Deep Link ”对话框。
  3. 在URI字段中键入URI,例如“www.cashdog.com/sendmoney”,它表示应用程序中发送货币嵌套图的起始目标。请注意以下事项:
    • 没有scheme的URI被假定为http和https。例如 www.cashdog.com 匹配 http://www.cashdog.com 和https://www.cashdog.com。
    • 占位符以{placeholder_name}的形式匹配1个或多个字符。占位符的String值在参数Bundle中可用,并带有相同名称的键。例如http://www.example.com/users/{id} 匹配 http://www.example.com/users/4
    • .*通配符可用于匹配0个或多个字符
  4. (可选)选中“自动验证”以要求Google验证URI的所有者
  5. 单击添加。所选目标上方会显示一个链接图标,表示该目标具有Deep Link
  6. 单击Text选项卡以切换到XML视图。已将嵌套的Deep Link元素添加到目标.
<deepLink app:uri="https://cashdog.com/sendmoney"/>

为Deep Link添加意图过滤

你必须在manifest.xml文件增加额外内容来启用Deep Link。

<activity name=".MainActivity">
    <nav-graph android:value="@navigation/main_nav" />
</activity>

作为清单合并构建步骤的一部分,此元素将替换为匹配导航图中所有deeplink所需的生成的元素

以编程方式使用NavDeepLinkBuil创建Deep Link

你可以使用NavDeepLinkBuilder类构造PendingIntent,将用户带到特定目标界面。 触发此deep link时,将清除任务后台堆栈并替换为deep link目标界面。

嵌套图形时,每个嵌套级别的起始目标(即层次结构中每个元素的起始目标)也会添加到堆栈中。 你可以使用NavDeepLinkBuilder(Context)直接构造PendingIntent,如下例所示。请注意,如果提供的上下文不是Activity,则构造函数使用PackageManager.getLaunchIntentForPackage()作为要启动的默认Activity(如果可用)。

PendingIntent pendingIntent = new NavDeepLinkBuilder(context)
    .setGraph(R.navigation.mobile_navigation)
    .setDestination(R.id.android)
    .setArguments(args)
    .createPendingIntent();

在目的地之间创建过渡效果

添加方式和上面添加deep link或者action操作类似,操作结果代码如下:

<fragment
    android:id="@+id/specifyAmountFragment"
    android:name="com.example.buybuddy.buybuddy.SpecifyAmountFragment"
    android:label="fragment_specify_amount"
    tools:layout="@layout/fragment_specify_amount">
    <action
        android:id="@+id/confirmationAction"
        app:destination="@id/confirmationFragment"
        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架构组件还支持在目标之间添加共享元素过渡。 与动画不同,共享元素是以编程方式提供的,而不是通过导航XML文件提供的,因为它们需要引用你希望包含在共享元素转换中的View实例。 每种类型的目标都通过Navigator.Extras接口的子类实现此编程API。 Extras被传递给navigate()的调用。

Fragment间共享元素

FragmentNavigator.Extras类允许您将共享元素附加到对Fragment目标的navigate()调用,如下例所示:

FragmentNavigator.Extras extras = new FragmentNavigator.Extras.Builder()
    .addSharedElement(imageView, "header_image")
    .addSharedElement(titleView, "header_title")
    .build();
Navigation.findNavController(view).navigate(R.id.details,
    null, // Bundle of args
    null, // NavOptions
    extras);

Activity间共享元素

活依赖于ActivityOptionsCompat来控制共享元素转换,详见使用共享元素文档启动活动,如下例所示