Jetpack 之 Navigation 初探

简介

Google 2018 I/O大会上,Google正式推出了Android/Jetpack,其中隆重推出了一个新的架构组件:Navigation

Google 官方介绍:

作为构建您的应用内界面的框架,重点是让单 Activity 应用成为首选架构。利用 Navigation 组件对 Fragment 的原生支持,您可以获得架构组件的所有好处(例如生命周期和 ViewModel),同时让此组件为您处理 FragmentTransaction 的复杂性。此外,Navigation组件还可以让您声明我们为您处理的转场。它可以自动构建正确的“向上”和“返回”行为,包含对深层链接的完整支持,并提供了帮助程序,用于将导航关联到合适的 UI 小部件,例如抽屉式导航栏和底部导航。

集成环境

Android Studio 3.2+
下载地址:https://developer.android.com/studio/preview

添加依赖

Step-1:

repositories 添加 Google 仓库和 classpath

  buildscript {
    repositories {
        google()
    }
    dependencies {
        classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0-alpha01"
    }
}

Step-2:

添加 Navigation 库的依赖

dependencies {
    def nav_version = "1.0.0-alpha01"

    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
}

新建 Navigation

  • 新建 Android 项目。
  • res 目录右键 New->New Resource File,弹出 New Resource File 的对话框。
  • 填写 File Name 如:nav_graph,Resource type 选择 Navigation,点击OK( Android Studio 3.3新建项目会自动生成该目录)。

以上操作会在 res 下生成一个 navigation 目录,目录下有刚才新建的 nav_graph.xml 文件,打开该文件,内容是一个 navigation 的空节点。和布局文件类似,AndroidStudio 界面上有。

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android">

</navigation>

使用 Navigation

Step-1:

新建两个 Fragment:FragmentA、FragmentB 对应布局为 fragment_a.xml、fragment_b.xml

Step-2:

打开 nav_graph.xml,底部选择 Design 选项卡,点击 New Destination (左上角 + ) 按钮,在弹窗中选择
fragment_a.xmlfragment_b.xml,或选择 Create blank destination 新建Fragment.之后选显示如下界面:

image

此时 nav_graph.xml 内容如下,点击 Text 切换

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:tools="http://schemas.android.com/tools"
    app:startDestination="@id/fragmentA">

    <fragment
        android:id="@+id/fragmentA"
        android:name="com.halove.jetpackdmeo.FragmentA"
        android:label="fragment_a"
        tools:layout="@layout/fragment_a" >
    </fragment>
    <fragment
        android:id="@+id/fragmentB"
        android:name="com.halove.jetpackdmeo.FragmentB"
        android:label="fragment_b"
        tools:layout="@layout/fragment_b" />
</navigation>

Step-3:

在 Activity 布局里添加 NavHostFragment (关于 NavHostFragment 可以看这篇博客,解释的很清楚 https://blog.csdn.net/mq2553299/article/details/80445952)

<?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 。app:navGraph="@navigation/nav_graph"将 NavHostFragment 跟我们刚才创建的 navigation 关联。
然后重新打开 nav_graph.xml 会发现在 HOST 下面就会显示我们关联的 activity:

image

Step-4:

添加导航连接

左键按住 fragment 右侧中间的圆圈然后拖动到要导航的 fragment 然后松手

image

切换到 Text 模式下,发现在 fragment 标签里添加了一个 action 节点,action 添加了一个 id 和 destination ,destination 就是我们要导航到的 fragment。

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:tools="http://schemas.android.com/tools"
    app:startDestination="@id/fragmentA">

    <fragment
        android:id="@+id/fragmentA"
        android:name="com.halove.jetpackdmeo.FragmentA"
        android:label="fragment_a"
        tools:layout="@layout/fragment_a" >
        <action
            android:id="@+id/action_fragmentA_to_fragmentB"
            app:destination="@id/fragmentB" />
    </fragment>
    <fragment
        android:id="@+id/fragmentB"
        android:name="com.halove.jetpackdmeo.FragmentB"
        android:label="fragment_b"
        tools:layout="@layout/fragment_b" />
</navigation>

Activity 中不需要做任何操作,只需要设置布局即可:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
    }
}

在 FragmentA 中布局中添加一个 Button ,点击跳转到 FragmentB :

  override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
   
        to_fragmentb_btn.setOnClickListener {
            Navigation.findNavController(it).navigate(R.id.action_fragmentA_to_fragmentB)
        }
        to_fragmentb_btn.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_fragmentA_to_fragmentB))
 }

使用很简单,调用 Navigation 的 findNavController 方法找到 NavController , findNavController 还有其他参数的方法大家可以自己试试,然后调用 navigate 方法,参数就是 nav_graph.xml 里 actionid 。或者直接使用 createNavigateOnClickListener.

按返回键会回退到上一个 Fragment ,也可以调用 NavController 的 popBackStack 进行回退

页面传参

1 .代码传参

navigate 有好几个方法,如图所示:

image

可以使用Bundle传参

 val bundle = Bundle()
 bundle.putString("param", "I AM FROM FRAGMENT-A")
 Navigation.findNavController(it).navigate(R.id.action_fragmentA_to_fragmentC,bundle)

2. xml 文件传参

在 navigation 的xml的 fragment 的 action 里添加 argument 标签,然后使用生成的对应的 Agrs 或者 Directions 来传递参数,需要在 build.gradle 中添加 apply plugin: 'androidx.navigation.safeargs'

<fragment
    android:id="@+id/fragmentB"
    android:name="com.halove.jetpackdmeo.FragmentB"
    android:label="fragment_b"
    tools:layout="@layout/fragment_b" >
    <argument android:name="text" android:defaultValue="Hello" app:type="string"/>
</fragment>

传参:

FragmentBArgs 和 FragmentADirections 都是自动生成的,FragmentBArgs 是根据fragment 节点下的 argument 节点生成的,FragmentADirections 是根据 action 生成的

//使用FragmentBArgs
val bundle = FragmentBArgs.Builder().setText("Hello World").build().toBundle()
Navigation.findNavController(it).navigate(R.id.action_fragmentA_to_fragmentB, bundle)

//使用FragmentADirections
val direction = FragmentADirections.action_fragmentA_to_fragmentB().setText("Hello World")
Navigation.findNavController(it).navigate(direction)

3. 参数接收

val text = FragmentBArgs.fromBundle("key").text
//或
val text =  arguments!!["key"].toString()

转场动画

转场动画可以直接在 action 里面使用动画文件:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    app:startDestination="@id/fragmentA">

    <fragment
        android:id="@+id/fragmentA"
        android:name="com.halove.jetpackdmeo.FragmentA"
        android:label="fragment_a"
        tools:layout="@layout/fragment_a" >
        <action
            android:id="@+id/action_fragmentA_to_fragmentB"
            app:destination="@id/fragmentB"
            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>
    <fragment
        android:id="@+id/fragmentB"
        android:name="com.halove.jetpackdmeo.FragmentB"
        android:label="fragment_b"
        tools:layout="@layout/fragment_b" >
        <argument android:name="text" android:defaultValue="Hello" app:type="string"/>
    </fragment>
</navigation>

Demo

附上使用小 Demo

地址:https://github.com/wanglejun/NavigationDemo.git

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容