Android Jetpack架构组件之Navigation

5,435 阅读5分钟

1、前言

最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面。

Android Architecture组件是Android Jetpack的一部分,它们是一组库,旨在帮助开发者设计健壮、可测试和可维护的应用程序,包含一下组件:

上述时Android Architecture所提供的架构组件,本文一起来看一下Navigation组件的使用。

2、Navigation简介

导航架构组件简化了Android应用程序中导航的实现,通过在xml中添加元素并指定导航的起始和目的地,从而在Fragment之间建立连接在Activity中调用xml中设置的导航action从而跳转界面到目的地,简单来说它和之前在活动中调用startActivity的区别就类似于代码布局和xml中layout布局一样,既简单又可视化,如下图就是一个navigaton的xml图:

Navigation多数作用于Fragment中,不过导航组件还支持:Fragment、Activity、导航图和子图、自定义目标

3、Navigation设置操作

3.1、在项目中设置Navigation

  • 开启Navigation支持
  1. 点击 File -》 Setting -》Experimental -> 选中 Enable Navigation Editor -> Restart Studio
  • 添加项目组件依赖
dependencies {
def nav_version = "1.0.0-alpha06"

implementation "android.arch.navigation:navigation-fragment:$nav_version"
implementation "android.arch.navigation:navigation-ui:$nav_version"
androidTestImplementation "android.arch.navigation:navigation-testing:$nav_version"
}
  • 创建 xml 文件

3.2、Navigation编辑器

  • Navigation编辑器界面

  • Navigation编辑器的三个部分:
  1. “目标”列表 - 列出“曲线图编辑器”中当前的所有目标
  2. 图表编辑器 - 包含Navigation图的可视化表示
  3. 属性编辑器 - 包含与Navigation图中的目标和操作关联的属性

3.3、确定目的地

  • 要确定应用的目标,请使用以下步骤
  1. 从图形编辑器,单击新目的地 。出现“ 新目标”对话框
  2. 单击“ 创建空白目标”或单击片段或活动。将出现“新建Android组件”对话框
  3. 在“ 片段名称”字段中输入名称。此名称是片段类的名称
  4. 在“ 片段布局名称”字段中输入名称。此名称是片段的布局文件的名称,单击完成
  • Attributes面板中显示以下属性
  1. Type:字段包含“Fragment”或“Activity”,以指示目标是否在源代码中实现为片段或活动
  2. Label:字段包含目标的XML布局文件的名称
  3. ID:字段包含将用于在代码中引用目标的目标ID
  4. Class:字段包含目标类的名称

上面的属性选中后都会自动生成xml中的代码,如:

 <fragment android:id="@+id/blankFragment" 
           android:name="com.example.administrator.navigation.BlankFragment"
           android:label="fragment_blank" 
           tools:layout="@layout/fragment_blank">
</fragment>

3.4、连接目的地

  • 连接
  1. 在图表编辑器中,将鼠标悬停在您希望用户导航的目标的右侧。目的地上会出现一个圆圈
  2. 单击并按住,将光标拖动到希望用户导航到的目标上,然后释放。绘制一条线以指示两个目的地之间的导航
  3. 此时Fragment会添加<action>标签

<action
    android:id="@+id/action_otherFragment_to_blankFragment"  // 在导航时调用此ID设置目的
    app:destination="@id/blankFragment" />
  • 单机箭头显示操作属性
  1. Type:操作类型
  2. ID:系统为操作分配的ID
  3. Destination:目标片段的id

  • 切换起始目的地
  1. 界面加载的第一个目标名称旁边放置一个房屋图标
  2. 点击对应的Fragment在右边属性中(或右击)点击“Set Start Destination“

4、实现导航

4.1、修改活动导航

  1. 活动通过实现NavHost 添加到活动布局的界面来托管应用程序的导航, NavHost是一个空视图,当用户浏览您的应用程序时,目的地会被换入和换出,默认 NavHost实现是 NavHostFragment
  2. 将导航图与NavHostFragment 使用navGraph属性相关联
  • xml中设置导航
  1. 在xml中设置navGraph的资源文件
  2. app:defaultNavHost="true”属性,可确保您NavHostFragment拦截系统“后退”按钮
  3. 覆盖AppCompatActivity.onSupportNavigateUp() 并调用NavController.navigateUp
   //在Activity的xml中添加fragment
<fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/nav_graph"
        app:defaultNavHost="true"
        />
// 在Activity中设置Navcontroller
override fun onSupportNavigateUp()
        = findNavController(nav_host_fragment).navigateUp()
  • 代码创建 NavHostFragment实现导航
  1. 使用NavHostFragment.create() 以编程方式创建NavHostFragment
val finalHost = NavHostFragment.create(R.navigation.example_graph)

supportFragmentManager.beginTransaction()
    .replace(R.id.nav_host_fragment, finalHost)
    .setPrimaryNavigationFragment(finalHost) 
    .commit()

4.2、将目标绑定到UI小部件

使用NavController该类导航到目的地,系统提供以下获取NavController的方法:

  1. NavHostFragment.findNavController(Fragment)
  2. Navigation.findNavController(Activity, @IdRes int viewId)
  3. Navigation.findNavController(View)

检索 NavController,使用其 navigate() 方法传入navigation.xml中设置的导航action,执行导航到目标

btnFragment.setOnClickListener {
    Navigation.findNavController(btnFragment).navigate(R.id.action_blankFragment_to_secondFragment)
}

除了上面的navigation()方法外,还可以调用NavController.navigateUp() 和 NavController.popBackStack() 方法“向上”或“返回”,或使用 Navigation类的 createNavigateOnClickListener() 便捷方法导航到目标

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

执行上面的导航,点击按钮跳转到第二个Fragment

  • 将目标驱动绑定到菜单的UI组件
  1. 通过将目标id与XML中导航抽屉或溢出菜单项的id相同,将目标绑定到导航抽屉和溢出菜单
// 导航抽屉菜单xml文件
<item android:id="@id/secondFragment"
      android:title="Second"
      android:icon="@drawable/ic_launcher_foreground"
      android:menuCategory="secondary"
      app:showAsAction="always"
      />
  1. 使用NavigationUI 类的方法,可以将菜单中的项目连接到NavigationView
val navigationView = findViewById<NavigationView>(R.id.nav_view)
NavigationUI.setupWithNavController(navigationView, findNavController(mainBlankFragment))

运行结果:

  • 设置ToolBar
// Set up ActionBar
setSupportActionBar(binding.toolbar)
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)

5、导航的其他操作

5.1、在目的地之间传递数据

  • Bundle 方法
  1. 在图表编辑器中,单击接收参数的目标位
  2. 单击“属性”面板的“参数”部分中的“ Add(+)”
  3. 双击名称并输入参数的名称
  4. 按Tab并输入参数的默认值
  5. 单击此目标之前的操作。参数默认值应包含新添加的参数
  6. 单击“ 文本”选项卡以切换到XML视图

此时的xml中添加了参数

<argument
    android:name="value"
    app:type="integer"
    android:defaultValue="0" /

代码中,使用navigate() 方法创建一个Bundle并将其传递到目标

val bundle = Bundle()
bundle.putString("name","Blank")
bundle.putInt("number",10)
Navigation.findNavController(btnFragment).navigate(R.id.action_blankFragment_to_secondFragment,bundle)

// 在接收的代码中,使用该 getArguments()方法检索包并使用其内容
val tv = view.findViewById(R.id.textViewAmount)
text.text = "Name = ${arguments?.getString("name")} 
Number = ${arguments?.getInt("number")}"

5.2、将目标分组为嵌套导航图

  1. 可以将一系列目的地分组为导航图中的子图。子图称为“ 嵌套图 ”,而包含图称为“ 根图“
  • 要将目标分组到嵌套图中
  1. 在“曲线图编辑器”中,按住shift并单击要包含在嵌套图形中的目标
  2. 打开上下文菜单,然后选择“ 移动到嵌套图形” >“ 新建图形”。目标包含在嵌套图中
  3. 双击嵌套图。将显示嵌套图中的目标,在“目标”列表中,单击“ 根”以返回到根导航图
<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/navigation"
            app:startDestination="@id/blankFragment">
    <fragment  ....../>
    <navigation android:id="@+id/navigation2" app:startDestination="@id/blankFragment">
        <fragment android:id="@+id/blankFragment" android:name="com.example.administrator.navigation.BlankFragment"
            ......
        </fragment>
    </navigation>
</navigation>

此时预览图发生以下变化:

  • include
  1. 可以使用引用其他图形<include>
  2. 虽然这在功能上与使用嵌套图形相同,但<include>允许您使用其他项目模块或库项目中的图形

下面我们将上面的两个navigation并使用include引用,拆分如下:

  1. navigation.xml:将两个Fragment的跳转放在navigation中
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
          ......>
    <fragment android:id="@+id/blankFragment" android:name="com.example.administrator.navigation.BlankFragment"
              android:label="fragment_blank" tools:layout="@layout/fragment_blank">
      ......
       
      <action android:id="@+id/action_blankFragment_to_include" app:destination="@id/secondActivity"/>    // 跳转到SecondActivity
     //<action android:id="@+id/action_blankFragment_to_include" app:destination="@id/include"/>  // 跳转到include
     **:注意这里无论设置哪个,只要是跳转到include中,就会首先到app:startDestination的界面中
    </fragment>
<include  app:graph="@navigation/include"/>
</navigation>
  1. include.xml:将对SecondActivity的跳转放在include中
<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/include" app:startDestination="@id/secondActivity"> // 将include的startDestination设置为SecondActivity所以直接跳到SecondActivity    // 必须设置否则会报异常 no start destination defined via app:startDestination  
        <activity android:id="@+id/secondActivity" android:name="com.example.administrator.navigation.SecondActivity"
                  android:label="activity_second" tools:layout="@layout/activity_second"/>

</navigation>

5.3、目标创建深层链接

  • 为目标分配深层链接:点击右边属性框中的Deep Links 填写Url
<deepLink app:uri="https://cashdog.com/sendmoney"/>
  • 接添加intent过滤器
  1. 对于Android Studio 3.1之前,必须手动添加intent-filter 元素
  2. 对于Android Studio 3.2+,像Activity元素中添加nav-graph元素
<activity name=".MainActivity">
<nav-graph android:value="@navigation/main_nav" />
</activity>

5.4、目的地之间创建过渡

  1. 点击导航的连接线,在右侧属性菜单的Transition中修改过度动画
  2. 修改后的动画会自动添加到Navigation的代码中

此时xml中会自动补充代码:

app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"

5.5、条件导航

  • 实施条件导航

  1. 调用popBackStack()导航回原始目的地(如Login后返回Profile
  • 共同目的地
  1. 程序中多个元素访问公共的目标
  2. 在“曲线图编辑器”中,单击目标以突出显示目标
  3. 右键单击目标以显示上下文菜单
  4. 选择添加操作>全局

此时代码自动填充:

// 在最外层自动添加
<action android:id="@+id/action_global_secondFragment2" app:destination="@id/secondFragment"/>

使用共同目标

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

6、实战

使用Navigation,在Activity实现各自Fragment的导航以及两个Activity间的导航

  • 创建SecondActivity的导航文件 second.xml:引入了两个Fragment之间的切换
<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/second" app:startDestination="@id/fragmentT">

    <fragment android:id="@+id/fragmentT" android:name="com.example.administrator.navigation.FragmentT"
              android:label="fragment_fragment_t" tools:layout="@layout/fragment_fragment_t">
        <action android:id="@+id/action_fragmentT_to_fragmentOther" app:destination="@id/fragmentOther"/>
    </fragment>

    <fragment android:id="@+id/fragmentOther" android:name="com.example.administrator.navigation.FragmentOther"
              android:label="fragment_fragment_other" tools:layout="@layout/fragment_fragment_other"/>
</navigation>
  • 在navigation文件中添加跳转到second.xml的Action
<action android:id="@+id/action_blankFragment_to_secondActivity2" app:destination="@id/second"/>  
// 跳转到second导航
  • 在MainActivity的Fragment中调用Action,跳转到SecondActivity
btnActivity.setOnClickListener {
    val option = ActivityOptionsCompat.makeSceneTransitionAnimation(activity as Activity, imageView, "image")
    val exeras = ActivityNavigator.Extras(option)
    Navigation.findNavController(btnFragment)
        .navigate(R.id.action_blankFragment_to_secondActivity2, null, null, exeras)

到此Navigation的使用介绍就结束了,Navigation的使用确实使应用导航的设计变得更加简单,还有个更大的好处就是阅读性极好,在以往我们查看项目时,要想得到导航需要在代码中就行查看,现在直接可以查看xml文件即可,是不是很方便,好了Android Jetpack组件快要介绍完了,敬请期待

本文Navigation的Demo,欢迎Star