Jetpack 之 Navigation 初探

七彩祥雲至尊寶發表於2019-04-30

簡介

Google 2018 I/O大會上,Google正式推出了Android/Jetpack,其中隆重推出了一個新的架構元件:Navigation

Google 官方介紹:

作為構建您的應用內介面的框架,重點是讓單 Activity 應用成為首選架構。利用 Navigation 元件對 Fragment 的原生支援,您可以獲得架構元件的所有好處(例如生命週期和 ViewModel),同時讓此元件為您處理 FragmentTransaction 的複雜性。此外,Navigation元件還可以讓您宣告我們為您處理的轉場。它可以自動構建正確的“向上”和“返回”行為,包含對深層連結的完整支援,並提供了幫助程式,用於將導航關聯到合適的 UI 小部件,例如抽屜式導航欄和底部導航。

整合環境

Android Studio 3.2+ 下載地址:developer.android.com/studio/prev…

新增依賴

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 可以看這篇部落格,解釋的很清楚 blog.csdn.net/mq2553299/a…)

<?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

地址:github.com/wanglejun/N…

相關文章