【譯】Android Jetpack 架構(gòu)導(dǎo)航組件之Navigation

示例代碼:android-navigation
介紹:Navigation Codelab
原文:navigation-implementing

導(dǎo)航架構(gòu)組件簡化了App內(nèi)不同目標(biāo)的導(dǎo)航實現(xiàn)。一個目標(biāo)即是App內(nèi)一個特定的屏幕。導(dǎo)航架構(gòu)組件默認(rèn)包含了對fragments和Activities的支持,但是你也可以添加對新類型目標(biāo)的支持。一系列的目標(biāo)集合組成了app的導(dǎo)航圖。

另外,導(dǎo)航圖內(nèi)不同目標(biāo)的連接被稱作"actions"。圖1展示了示例app內(nèi)一個假設(shè)的導(dǎo)航圖表現(xiàn),包含了6個目標(biāo),由5個actions連接。

圖1 navigation-graph.png

導(dǎo)航架構(gòu)組件是基于Principles of nagivation實現(xiàn)的。

Set up nagivation in a project

在你創(chuàng)建一個導(dǎo)航圖之前,你必須在你的工程內(nèi)配置導(dǎo)航架構(gòu)組件。以下是具體步驟:

  1. 在你的app或者模塊的build.gradle文件內(nèi)天井Navigation Architecture Component。點擊Adding components to your project查看更多。
  2. 在工程窗口,右擊 res 目錄,選擇 New > Android resource file。顯示New Resource對話框。
  3. 輸入資源名稱在File name欄目,比如“nav_graph”。
  4. 選擇NavigationResource type下拉列表。
  5. 點擊 OK。出現(xiàn)以下項目:
    a. 一個 nagivation 資源目錄在res目錄下創(chuàng)建。
    b. 一個nav_graph.xml文件在navigation目錄下創(chuàng)建。
    c. nav_graph.xml文件將在Navigation Editor中打開。這個文件包含了你的導(dǎo)航圖。
  6. 點擊Texttab可以打開或者關(guān)閉text View??諏?dǎo)航圖如下:

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

  1. 點擊 Design返回導(dǎo)航編輯器。

Tour The Navigation Editor

Note: The Navigation Editor是在Android Studio的Canary Build中默認(rèn)開啟的。為了在Beta,Release Candidate和Stable Builds 中啟用,在Mac上點擊File > Settings(Android Studio > Preferences),在左側(cè)面板選擇Experimentalcategory,選擇Enable Navigation Editor,最后重啟Android Studio。

圖2 navigation-editor.png

在導(dǎo)航編輯器,你可以快速地構(gòu)建導(dǎo)航圖,不需要手動構(gòu)建graph’s xml文件。按照圖2顯示的,導(dǎo)航編輯器分為三個部分。
1. 目標(biāo)列表-羅列當(dāng)前導(dǎo)航編輯器內(nèi)所有目標(biāo)。
2. 圖編輯器-包含導(dǎo)航圖內(nèi)所有虛擬目標(biāo)。
3. 屬性編輯器-包含目標(biāo)屬性和導(dǎo)航圖內(nèi)的actions。

Identify destinations

為你的app驗證目標(biāo)是創(chuàng)建導(dǎo)航圖的第一部。你可以創(chuàng)建一個空目標(biāo)或者在一個已經(jīng)存在的工程內(nèi)為fragments和Acitvities創(chuàng)建目標(biāo)。

Note:導(dǎo)航架構(gòu)組件是為一個activity內(nèi)包含多個fragment的場景而設(shè)計的。主activity擁有導(dǎo)航圖。在一個擁有眾多activity目標(biāo)的app內(nèi),每一個附加的activity都擁有自己的導(dǎo)航圖。修改一個activity到主導(dǎo)航將在之后的文檔進(jìn)行討論。

請參照一下步驟驗證app內(nèi)目標(biāo):

  1. 在圖編輯器,點擊** New Destination。New Destination** 顯示。
  2. 點擊Create blank destination或者點擊一個fragment或者activity。會出現(xiàn)新的Android Component 對話框。
  3. Fragment Name字段輸入名字。這個名字是fragment的類的名字。
  4. Fragment layout Name字段輸入一個名字。這個名字是fragment布局文件的名字。
  5. 點擊完成,在圖標(biāo)編輯器的目標(biāo)列表出現(xiàn)一個目標(biāo),出現(xiàn)以下現(xiàn)象:
  • 如果你創(chuàng)建一個空白的目標(biāo),在圖標(biāo)編輯器出現(xiàn)一個空白的一個“Hello blank fragment” 的信息的目標(biāo),如果你點擊了一個Fragment或者Activity,圖標(biāo)編輯器會顯示對應(yīng)的布局。
  • 一個fragment子類將會被創(chuàng)建,名字在第三步被指定。
  • 一個資源文件會被創(chuàng)建,名字在第四步被指定。

圖3 展示了一個空白的已經(jīng)存在的目標(biāo)。


圖3 navigation-newexisting.png
  1. 點擊高亮顯示新插入的目標(biāo)。在屬性面板顯示以下屬性。
  • 在類型字段顯示“fragment”或者“activity”,表示目標(biāo)是否執(zhí)行了fragment或者activity。
  • 標(biāo)簽字段表示了目標(biāo)布局文件的名字;
  • ID字段表示了目標(biāo)的ID,用來在代碼中被引用;
  • 類字段表示了引用的目標(biāo)的類名;
  1. 點擊Text 進(jìn)入xml視圖,XML現(xiàn)在包含id,name(class name),label和layout屬性,基于已經(jīng)存在的類和布局文件的名字。
<?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>

Note:XML布局startDestination 表示一個空目標(biāo)的Id,(app:startDestination="@+id/fragment")。查詢更多關(guān)于startDestination信息請點擊 Designate a screen as the start destination。

Connect destinations

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

通過action連接不同的destination:
1、在Graph Editor,當(dāng)鼠標(biāo)浮動在destination的右邊緣時會顯示一個圈;
2、點擊并且拖動圓圈到另一個destination,一條線將會關(guān)聯(lián)起兩個destination;


圖5navigation-connected.png

3、點擊高亮箭頭,屬性面板顯示以下屬性:

  • 類型字段包含“Action”;
  • ID字段表示了系統(tǒng)為action指定的ID;
  • Destination字段表示了目標(biāo)Activity或者Fragment;
    4、代碼展示:
<?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>

Designate a screen as the start destination

Graph Editor中,進(jìn)入app的第一個Destination會有一個house的icon。通過一下步驟可以將另外一個Destination指定為啟動Destination。
1、點擊高亮顯示某個Destination;
2、點擊屬性面板的Set Start Destination即可完成設(shè)置;

Modify an activity to host navigation

一個Activity通過NavHost的實現(xiàn)類來持有app的導(dǎo)航。NavHost是一個空的View,因此Destinations會根據(jù)用戶的導(dǎo)航被交換進(jìn)或者退出。
Navigation Architecture Component 的默認(rèn)的NavHost的實現(xiàn)是NavHostFragment。
在你包含了NavHost之后,你必須使用navGraph屬性將NavHostFragment和Navigation graph關(guān)聯(lián)起來。下面是代碼示例:

<?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可以攔截系統(tǒng)的回退事件。你也可以重寫AppCompatActivity.onSupportNavigateUp()方法并且調(diào)用NavController.navigateUp方法:

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

Create the NavHostFragment programmatically

你可以使用NavHostFragment.create()
編碼創(chuàng)建NavHostFragment ,關(guān)聯(lián)一個graph資源,如下所示:

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();

Tie destinations to UI widgets

通過NavController
實現(xiàn)對Destination的導(dǎo)航。通過以下靜態(tài)方法獲取NavController示例:

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

Android 系統(tǒng)維護(hù)了一個back stack來表示最近訪問的destination。
在app啟動得時候,第一個destination會被放入棧里面。每一次調(diào)用navigate()
都會放入另外一個destination在棧的頂部。相反的,調(diào)用NavController.navigateUp()
NavController.popBackStack()
方法,從棧里彈出頂部的destination。對于按鈕,你可以使用Navigation
類的 createNavigateOnClickListener() 方法來很方便地去導(dǎo)航到destination。

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

Tie destinations to menu-driven UI components

通過為destination、navigation drawer和overflow menu設(shè)置相同的Id來將destination和navigation drawer或者overflow menu關(guān)聯(lián)起來。以下代碼片段顯示了id為details_page_fragment的destination:

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

以下代碼片段表示了如何關(guān)聯(lián)fragment destination 和navigation drawer中的menu item,比如( menu_nav_drawer.xml)。

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

以下XML表示了如果把destination和overflow menu 關(guān)聯(lián)的細(xì)節(jié):

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

Navigation Architecture Component 包含了一個NavigationUI
類。這個類的多個靜態(tài)方法都可以用來關(guān)聯(lián) menu item 和 navigation destination。以下代碼表示了如果使用setupWithNavController()
方法去關(guān)聯(lián) menu item 到Navigation View。

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

使用NavigationUI
去設(shè)置menu 驅(qū)動的導(dǎo)航組件是很有必要的,因為這樣可以同步這些UI元素地改變到NavController。

Pass data between destinations

兩種方式在不同的destination之間傳遞數(shù)據(jù):使用Bundle
對象或者通過Gradle插件類型安全得傳遞數(shù)據(jù)。遵照以下步驟在destinations之間使用Bundle對象傳遞數(shù)據(jù)。如果你想使用Gradle,請遵照Pass data between destinations in a type-safe way
的介紹。
1、在Graph Editor,點擊接受參數(shù)的destination。該destination會高亮。
2、在屬性面板的參數(shù)部分點擊 Add(+),會顯示空名字和默認(rèn)值字段。
3、在名字字段雙擊輸入?yún)?shù)名字。
4、按壓Tab,輸入?yún)?shù)的默認(rèn)值。
5、點擊上述destination的action。參數(shù)的默認(rèn)值應(yīng)該包含你新增的參數(shù)。
6、點擊Text去切換XML View。一個參數(shù)元素,包含名字和默認(rèn)值屬性,已經(jīng)被添加到該destination。

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

當(dāng)你使用safeargs plugin,會為action,destinations的接受者和發(fā)送者創(chuàng)建簡單對象和構(gòu)造類。這些類是:

  • 一個類為action源起的destination,后綴“Directions”。因此,如果起始的fragment叫做SpecifyAmountFragment,那么生成的類就叫做SpecifyAmountFragmentDirections。這個類有一個以傳遞參數(shù)的action來命名,去綁定參數(shù),比如confirmationAction()。
  • 一個匿名內(nèi)部類,名字基于傳遞參數(shù)的action。如果傳遞的action叫做confirmationAction,類就被命名為ConfirmationAciton。
  • 一個類用于接受destination,后綴Args,所以,如果destination fragment叫做ComfirmationFragment,生成的類就叫做ConfirmationFragmentArgs。使用這個類的fromBundle()方法獲取參數(shù)。
    示例代碼:
@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);
}
@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 + "")
}

Group destinations into a nested navigation graph

在navigation graph內(nèi),可以把一些相關(guān)的destination是分組為sub-graph,如果包含sub-graph的是root-graph,那么這個sub-graph被稱作“nestedgraph”,Nestedgraph在組織和重用App UI的模塊上是很有用的,比如登錄流程。
和root graph一樣,nested graph必須也要有一個啟動的destination作為唯一的標(biāo)識。nested graph封裝了它的destinations。nested graph外部,root graph之內(nèi)的destination只能通過root graph的啟動destination訪問nested graph。圖6 展示了一個simple money transfer app的navigation graph。這個graph有兩個流程:轉(zhuǎn)錢流程和余額流程。

圖6 navigation-pre-nestedgraph.png

把destinations分組為nested graph:
1、在graph editor,按住shift點擊需要加入nested graph的destination,該destination高亮。
2、打開菜單選擇Move to Nested Graph > New Graph,如圖7.
圖7navigation-nestedgraph.png

3、點擊高亮nested graph,屬性面板顯示如下:

  • Type字段顯示“Nested Graph”。
  • Id字段是系統(tǒng)為nested graph指定的。Id用來在代碼中引用這個nested graph。
    4、雙擊nested graph查看內(nèi)部的destinations。
    5、在destinations列表,點擊root返回root graph。
    6、點擊Text進(jìn)入XML視圖。
<?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>

7、在你的代碼里,使用action id來關(guān)聯(lián)root graph 和 nested graph。

Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);

Reference other navigation graphs using <include>

在 navigation graph內(nèi),可以通過<include>引用其它navigation graph,其等價于使用nested graph。以此使用其它module或者library中的navigation。

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

<include>只有一個app:graph屬性,且不允許其它任何屬性。

Create a deep link for a destination

Add an intent filter for a deep link

Create a transition between destinations

AndroidX命名空間遷移
Verify Android App Links (Deep Links和Android App Links的區(qū)別)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容