示例代碼:android-navigation
介紹:Navigation Codelab
原文:navigation-implementing
導(dǎo)航架構(gòu)組件簡(jiǎn)化了App內(nèi)不同目標(biāo)的導(dǎo)航實(shí)現(xiàn)黎棠。一個(gè)目標(biāo)即是App內(nèi)一個(gè)特定的屏幕晋渺。導(dǎo)航架構(gòu)組件默認(rèn)包含了對(duì)fragments和Activities的支持,但是你也可以添加對(duì)新類型目標(biāo)的支持脓斩。一系列的目標(biāo)集合組成了app的導(dǎo)航圖木西。
另外,導(dǎo)航圖內(nèi)不同目標(biāo)的連接被稱作"actions"随静。圖1展示了示例app內(nèi)一個(gè)假設(shè)的導(dǎo)航圖表現(xiàn)八千,包含了6個(gè)目標(biāo),由5個(gè)actions連接燎猛。
導(dǎo)航架構(gòu)組件是基于Principles of nagivation實(shí)現(xiàn)的恋捆。
Set up nagivation in a project
在你創(chuàng)建一個(gè)導(dǎo)航圖之前,你必須在你的工程內(nèi)配置導(dǎo)航架構(gòu)組件重绷。以下是具體步驟:
- 在你的app或者模塊的build.gradle文件內(nèi)天井Navigation Architecture Component沸停。點(diǎn)擊Adding components to your project查看更多。
- 在工程窗口昭卓,右擊 res 目錄愤钾,選擇 New > Android resource file。顯示New Resource對(duì)話框候醒。
- 輸入資源名稱在File name欄目能颁,比如“nav_graph”。
- 選擇Navigation在Resource type下拉列表倒淫。
- 點(diǎn)擊 OK伙菊。出現(xiàn)以下項(xiàng)目:
a. 一個(gè) nagivation 資源目錄在res目錄下創(chuàng)建。
b. 一個(gè)nav_graph.xml文件在navigation目錄下創(chuàng)建昌简。
c. nav_graph.xml文件將在Navigation Editor中打開。這個(gè)文件包含了你的導(dǎo)航圖绒怨。 - 點(diǎn)擊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>
- 點(diǎn)擊 Design返回導(dǎo)航編輯器南蹂。
Tour The Navigation Editor
Note: The Navigation Editor是在Android Studio的Canary Build中默認(rèn)開啟的犬金。為了在Beta,Release Candidate和Stable Builds 中啟用,在Mac上點(diǎn)擊File > Settings(Android Studio > Preferences)晚顷,在左側(cè)面板選擇Experimentalcategory峰伙,選擇Enable Navigation Editor,最后重啟Android Studio该默。
在導(dǎo)航編輯器瞳氓,你可以快速地構(gòu)建導(dǎo)航圖,不需要手動(dòng)構(gòu)建graph’s xml文件栓袖。按照圖2顯示的匣摘,導(dǎo)航編輯器分為三個(gè)部分。
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驗(yàn)證目標(biāo)是創(chuàng)建導(dǎo)航圖的第一部捧弃。你可以創(chuàng)建一個(gè)空目標(biāo)或者在一個(gè)已經(jīng)存在的工程內(nèi)為fragments和Acitvities創(chuàng)建目標(biāo)赠叼。
Note:導(dǎo)航架構(gòu)組件是為一個(gè)activity內(nèi)包含多個(gè)fragment的場(chǎng)景而設(shè)計(jì)的。主activity擁有導(dǎo)航圖违霞。在一個(gè)擁有眾多activity目標(biāo)的app內(nèi)嘴办,每一個(gè)附加的activity都擁有自己的導(dǎo)航圖。修改一個(gè)activity到主導(dǎo)航將在之后的文檔進(jìn)行討論葛家。
請(qǐng)參照一下步驟驗(yàn)證app內(nèi)目標(biāo):
- 在圖編輯器户辞,點(diǎn)擊** New Destination。New Destination** 顯示癞谒。
- 點(diǎn)擊Create blank destination或者點(diǎn)擊一個(gè)fragment或者activity底燎。會(huì)出現(xiàn)新的Android Component 對(duì)話框。
- 在Fragment Name字段輸入名字弹砚。這個(gè)名字是fragment的類的名字双仍。
- 在Fragment layout Name字段輸入一個(gè)名字。這個(gè)名字是fragment布局文件的名字桌吃。
- 點(diǎn)擊完成朱沃,在圖標(biāo)編輯器的目標(biāo)列表出現(xiàn)一個(gè)目標(biāo),出現(xiàn)以下現(xiàn)象:
- 如果你創(chuàng)建一個(gè)空白的目標(biāo)茅诱,在圖標(biāo)編輯器出現(xiàn)一個(gè)空白的一個(gè)“Hello blank fragment” 的信息的目標(biāo)逗物,如果你點(diǎn)擊了一個(gè)Fragment或者Activity,圖標(biāo)編輯器會(huì)顯示對(duì)應(yīng)的布局瑟俭。
- 一個(gè)fragment子類將會(huì)被創(chuàng)建翎卓,名字在第三步被指定。
- 一個(gè)資源文件會(huì)被創(chuàng)建摆寄,名字在第四步被指定失暴。
圖3 展示了一個(gè)空白的已經(jīng)存在的目標(biāo)坯门。
- 點(diǎn)擊高亮顯示新插入的目標(biāo)。在屬性面板顯示以下屬性逗扒。
- 在類型字段顯示“fragment”或者“activity”古戴,表示目標(biāo)是否執(zhí)行了fragment或者activity。
- 標(biāo)簽字段表示了目標(biāo)布局文件的名字矩肩;
- ID字段表示了目標(biāo)的ID现恼,用來在代碼中被引用;
- 類字段表示了引用的目標(biāo)的類名蛮拔;
- 點(diǎn)擊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 表示一個(gè)空目標(biāo)的Id肛跌,(app:startDestination="@+id/fragment")艺配。查詢更多關(guān)于startDestination信息請(qǐng)點(diǎn)擊 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)浮動(dòng)在destination的右邊緣時(shí)會(huì)顯示一個(gè)圈;
2稳捆、點(diǎn)擊并且拖動(dòng)圓圈到另一個(gè)destination赠法,一條線將會(huì)關(guān)聯(lián)起兩個(gè)destination;
3乔夯、點(diǎn)擊高亮箭頭砖织,屬性面板顯示以下屬性:
- 類型字段包含“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的第一個(gè)Destination會(huì)有一個(gè)house的icon眶熬。通過一下步驟可以將另外一個(gè)Destination指定為啟動(dòng)Destination。
1块请、點(diǎn)擊高亮顯示某個(gè)Destination娜氏;
2、點(diǎn)擊屬性面板的Set Start Destination即可完成設(shè)置墩新;
Modify an activity to host navigation
一個(gè)Activity通過NavHost的實(shí)現(xiàn)類來持有app的導(dǎo)航贸弥。NavHost是一個(gè)空的View,因此Destinations會(huì)根據(jù)用戶的導(dǎo)航被交換進(jìn)或者退出抖棘。
Navigation Architecture Component 的默認(rèn)的NavHost的實(shí)現(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)一個(gè)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
實(shí)現(xiàn)對(duì)Destination的導(dǎo)航般渡。通過以下靜態(tài)方法獲取NavController示例:
NavHostFragment.findNavController(Fragment)
Navigation.findNavController(Activity, @IdRes int viewId)
-
Navigation.findNavController(View)
通過NavController
的navigate()
方法導(dǎo)航到destination。navigate()
方法接受一個(gè)資源Id的參數(shù)芙盘。該Id可以是一個(gè)特別的destination的Id或者是action Id驯用。相比destination的Id,使用action Id可以使用關(guān)聯(lián)的transition動(dòng)畫儒老。點(diǎn)擊Create a transition between destinations查詢更多關(guān)于transition信息蝴乔。
以下代碼示例展示了ViewTransactionsFragment的導(dǎo)航:
viewTransactionsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Navigation.findNavController(view).navigate(R.id.viewTransactionsAction);
}
});
Android 系統(tǒng)維護(hù)了一個(gè)back stack來表示最近訪問的destination。
在app啟動(dòng)得時(shí)候驮樊,第一個(gè)destination會(huì)被放入棧里面薇正。每一次調(diào)用navigate()
都會(huì)放入另外一個(gè)destination在棧的頂部。相反的囚衔,調(diào)用NavController.navigateUp()
和NavController.popBackStack()
方法挖腰,從棧里彈出頂部的destination。對(duì)于按鈕练湿,你可以使用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 包含了一個(gè)NavigationUI
類贤姆。這個(gè)類的多個(gè)靜態(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òng)的導(dǎo)航組件是很有必要的霞捡,因?yàn)檫@樣可以同步這些UI元素地改變到NavController坐漏。
Pass data between destinations
兩種方式在不同的destination之間傳遞數(shù)據(jù):使用Bundle
對(duì)象或者通過Gradle插件類型安全得傳遞數(shù)據(jù)。遵照以下步驟在destinations之間使用Bundle對(duì)象傳遞數(shù)據(jù)碧信。如果你想使用Gradle赊琳,請(qǐng)遵照Pass data between destinations in a type-safe way
的介紹越除。
1镰惦、在Graph Editor,點(diǎn)擊接受參數(shù)的destination专肪。該destination會(huì)高亮呈枉。
2趁尼、在屬性面板的參數(shù)部分點(diǎn)擊 Add(+)埃碱,會(huì)顯示空名字和默認(rèn)值字段。
3酥泞、在名字字段雙擊輸入?yún)?shù)名字砚殿。
4、按壓Tab芝囤,輸入?yún)?shù)的默認(rèn)值似炎。
5、點(diǎn)擊上述destination的action悯姊。參數(shù)的默認(rèn)值應(yīng)該包含你新增的參數(shù)羡藐。
6、點(diǎn)擊Text去切換XML View悯许。一個(gè)參數(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欧啤,會(huì)為action,destinations的接受者和發(fā)送者創(chuàng)建簡(jiǎn)單對(duì)象和構(gòu)造類启上。這些類是:
- 一個(gè)類為action源起的destination邢隧,后綴“Directions”。因此冈在,如果起始的fragment叫做SpecifyAmountFragment倒慧,那么生成的類就叫做SpecifyAmountFragmentDirections。這個(gè)類有一個(gè)以傳遞參數(shù)的action來命名包券,去綁定參數(shù)纫谅,比如confirmationAction()。
- 一個(gè)匿名內(nèi)部類溅固,名字基于傳遞參數(shù)的action付秕。如果傳遞的action叫做confirmationAction,類就被命名為ConfirmationAciton侍郭。
- 一個(gè)類用于接受destination询吴,后綴Args,所以亮元,如果destination fragment叫做ComfirmationFragment猛计,生成的類就叫做ConfirmationFragmentArgs。使用這個(gè)類的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煮甥,那么這個(gè)sub-graph被稱作“nestedgraph”盗温,Nestedgraph在組織和重用App UI的模塊上是很有用的藕赞,比如登錄流程。
和root graph一樣卖局,nested graph必須也要有一個(gè)啟動(dòng)的destination作為唯一的標(biāo)識(shí)找默。nested graph封裝了它的destinations。nested graph外部吼驶,root graph之內(nèi)的destination只能通過root graph的啟動(dòng)destination訪問nested graph。圖6 展示了一個(gè)simple money transfer app的navigation graph店煞。這個(gè)graph有兩個(gè)流程:轉(zhuǎn)錢流程和余額流程蟹演。
把destinations分組為nested graph:
1、在graph editor顷蟀,按住shift點(diǎn)擊需要加入nested graph的destination酒请,該destination高亮。
2鸣个、打開菜單選擇Move to Nested Graph > New Graph羞反,如圖7.
3、點(diǎn)擊高亮nested graph囤萤,屬性面板顯示如下:
- Type字段顯示“Nested Graph”昼窗。
- Id字段是系統(tǒng)為nested graph指定的。Id用來在代碼中引用這個(gè)nested graph涛舍。
4澄惊、雙擊nested graph查看內(nèi)部的destinations。
5富雅、在destinations列表掸驱,點(diǎn)擊root返回root graph。
6没佑、點(diǎn)擊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啤贩,其等價(jià)于使用nested graph扣溺。以此使用其它module或者library中的navigation。
<include app:graph="@navigation/included_graph"/>
<include>只有一個(gè)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ū)別)