Android JetPack Work Manager 和 Navigation 组件如何使用?

关注者
13
被浏览
3,867

3 个回答

看见这个问题没什么人回答,正好最近也在吃惊于Navigation的强大功能,就顺手过来回答一下吧。 本篇答案专注于Navigation组件该如何使用。

参考文献:
developer.android.com/t

前言

关于为什么要使用Navigation的原因,我认为有二:

  1. 需要构建单Activity多Fragment架构APP
  2. 需要使用深度链接Deep Link功能

#关于为什么要构建单Activity多Fragment架构的APP

我认为这是一个工程学上的Best Practice——业界权威Jake Wharton推荐,公认的优秀APP Airbnb在用,更不用提Google为此专门推出了Navigation组件。

#关于什么是深度链接

举一个例子:当我们在浏览知乎网页版知乎时,有一个「在APP中打开」,然后我们直接打开就是APP中该回答的那页,这使用的就是深度链接技术。

#回顾下传统的打开Activity和打开Fragment方式

  • Intent 打开Activity
  • 没有直接打开Fragment的方法,需要让Activity继承一个接口实现切换Fragment功能,然后在Fragment中使用Activity转型为该接口后的实例进行回调

#展望下现在的打开Activity和打开Fragment方式

  • 统一的navigate()方法

一、使用Navigation前我们要知道的三个名词

  • destination
  • actions
  • navigation graph

#1 destination

destination,中文直译为“目的地”,在这里可以指代三个内容:

  • Activity
  • Fragment
  • Navigation Graph

其中,destination通常应该代表Fragment。而Navigation Graph为何物此时我们先不必管。

#2 actions

actions,中文直译为“行为”,在这里指代 将两个destination连接起来的线

Destination 与 Action

如图所示,每一个矩形即destination,而连接它们的线就是action。

#3 Navigation Graph

准确的说,上面这张图就是一个Navigation Graph的预览图。Navigation Graph包含了Destination和Action,有时还会包含其它的Navigation Graph。下面是一个Navigation Graph的代码,以及它在项目中位置的截图:

<?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/blankFragment1">
    <fragment
        android:id="@+id/blankFragment1"
        android:name="com.tipchou.navigationdemo.BlankFragment1"
        android:label="fragment_blank1"
        tools:layout="@layout/fragment_blank1">
        <action
            android:id="@+id/action_blankFragment1_to_blankFragment2"
            app:destination="@id/blankFragment2"
            app:enterAnim="@anim/nav_default_enter_anim"
            app:exitAnim="@anim/nav_default_exit_anim"
            app:popEnterAnim="@anim/nav_default_pop_exit_anim"
            app:popExitAnim="@anim/nav_default_pop_enter_anim" />
        <argument
            android:name="fragment1"
            android:defaultValue="0" />
    </fragment>
    <fragment
        android:id="@+id/blankFragment2"
        android:name="com.tipchou.navigationdemo.BlankFragment2"
        android:label="fragment_blank2"
        tools:layout="@layout/fragment_blank2">
        <argument
            android:name="fragment2"
            android:defaultValue="0" />
        <deepLink app:uri="www.hearfresh.com/fuck" />
    </fragment>
</navigation>


二、配置Navigation到你的项目中

遵循以下步骤,在Android Studio中进行设定:

  1. 点击File->Setting,选择Experimental,勾选Enable Navigation Editor,然后重启Android Studio
  2. 配置Gradle添加Navigation组件,如下图所示:

3. 右键点击res文件夹然后选择New->Android Resource File,找到navigation并创建

三、介绍一些关键图标

#1 创建新的Destination

#2 使用Action连接Destination

#3 指定起始 Destination

四、指定一个Activity去Host整个Navigation Graph

#通过在Activity的layout文件中添加如下内容

<?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:id="@+id/my_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" />
</android.support.constraint.ConstraintLayout>

#app:defaultNavHost="true"

这个属性会确保你的NavHostFragment拦截系统的返回按钮,让它不会一下子退出Activity,而是退回到上一个Fragment。

五、导航至某一Destination

我们使用navigate()方法去导航至某一Destination。此方法接收一个resource ID,这个ID可以是navigation graph中的:

  • action的ID(推荐)
  • destination的ID

使用Action的ID更被推荐是因为——我们可以绑定一些transitions(过渡动画)。

#navigate方法

textview.setOnClickListener { view1 ->
    view1.findNavController().navigate(R.id.action_blankFragment1_to_blankFragment2)
}

#我们还可以通过创建一个OnClickListener对象来导航至某一Destination

button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.blankFragment1))

六、在Destination之间传递数据

我认为在Destination之间传输数据可以使用共享Acitivity的ViewModel的方式,这也是Google推荐的方式。但是Destination之间也有原生的传递数据的方式:

  • Bundle方式
  • type-safe方式

这里只介绍Bundle方式:

注:本例所用的bundleOf()方法是Jake Wharton所写的KTX库中函数

#1 选择一个Destination,点击Arguments上的小加号,输入name\type\default value

#2 在代码中,使用nativgate()方法发送数据

var bundle = bundleOf("amount" to "shit") //amount是一个String类型的数据
view.findNavController().navigate(R.id.confirmationAction, bundle)

#3 在代码中,使用getArguments()方法接收数据

val tv = view.findViewById(R.id.textViewAmount)tv.text = arguments.getString("amount")

七、添加一个Listener来确认目前屏幕上显示的是哪个Destination

这个技术我认为是比较有用的,我们使用单一Activity多Fragment架构时,会遇到一个这样的问题:

  • 有些界面是需要顶边栏和底边栏的,有些界面是不需要顶边栏和底边栏的

于是我们可以在Activity中添加一个监听器,动态的显示/隐藏顶边栏和底边栏。

#使用方法addOnNavigatedListener()

findNavController(R.id.my_nav_host_fragment)
                .addOnNavigatedListener { _, destination ->
                    when (destination.id) {
                        R.id.blankFragment1 -> {
                            Log.e(MainActivity::javaClass.name, "Fragment1")
                        R.id.blankFragment2 -> {
                            Log.e(MainActivity::javaClass.name, "Fragment2")
                }

八、创建一个深度链接(Deep Link)

这块我就不搬运谷歌的内容了,大家可以在文章上面给出的参考链接中自己寻找答案,我相信功夫不负有心人,如果作为一个Android开发者,连Google访问这个问题都解决不了的话————


那你还是要先解决一下为妙,因为国内的技术文章都是搬运,源头在Google。


就这样,再见。