在 Google I/O 2018 上新出現(xiàn)了一個導航組件(Navigation Architecture Component),導航組件類似iOS開發(fā)里的StoryBoard,可以可視化的編輯App頁面的導航關(guān)系。在經(jīng)過兩天的學習后,將心得總結(jié)在這里。
還沒有看過官方資料的童鞋看這里:
官方文檔:The Navigation Architecture Component
官方教程:Navigation Codelab
導航(Navigation)規(guī)則
- App需要有確定的起始點
- 使用一個棧來代表App的導航狀態(tài)
- 向上按鈕從不會退出你的App
- 在App任務(wù)中向上和返回按鈕是等價的
- 深度鏈接到目標或?qū)Ш降较嗤哪繕藨?yīng)產(chǎn)生相同的堆棧
使用
- 導航是為單Activity多Fragment的應(yīng)用設(shè)計的
- 導航似乎不能解決數(shù)據(jù)返回的問題
安裝Android Studio最新的預覽版 3.2 canary 14
app build.gradle中添加依賴:
implementation "android.arch.navigation:navigation-fragment-ktx:$nav_version"
implementation "android.arch.navigation:navigation-fragment:$nav_version" // use -ktx for Kotlin
implementation "android.arch.navigation:navigation-ui:$nav_version"
implementation "android.arch.navigation:navigation-ui-ktx:$nav_version" // use -ktx for Kotlin
添加導航圖(類似iOS開發(fā)中的StoryBoard):
- 右擊
res目錄,選擇New > Android resource file - 在New Resource對話中輸入文件名
nav_graph,選擇Resource type為Navigation
點擊OK后IDE會在navigation目錄下生成nav_graph.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android">
</navigation>

在Activity布局中指定Navigation的宿主(Host):
?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"
app:navGraph="@navigation/nav_graph"
app:defaultNavHost="true"
/>
</android.support.constraint.ConstraintLayout>
其中,fragment的name一定要是androidx.navigation.fragment.NavHostFragment,app:navGraph輸入剛剛生成的導航圖位置
覆寫onSupportNavigateUp()方法:
@Override
public boolean onSupportNavigateUp() {
return Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp();
}
添加導航的起始位置和目的位置:
打開導航圖編輯器,點擊新增按鈕,可以新建一個空白目的地或者選擇已有的Fragment或者Activity,完成后頁面的預覽圖就會顯示在編輯器里,同時IDE會給它指定默認的id等屬性。點擊Fragment右邊的手柄不放開,將它拖動到另一個頁面,一個操作(Action)就創(chuàng)建好了,IDE會給它分配一個默認的action id。在起始Fragment上點擊右鍵,選擇Set Start Destination,將它設(shè)置為起始位置,當宿主(Host)Activity啟動的時候,它會做為默認的頁面替換布局中的NavHostFragment。
導航圖新增目的地默認是新增Fragment,可以指定啟動模式,可以指定切換動畫、可以指定參數(shù)及其類型。點擊箭頭可以更改這些參數(shù)。也通過使用安全類型插件來生成對應(yīng)的代碼來保證參數(shù)類型安全:
project gradle:
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0-alpha01'
}
}
app gradle:
apply plugin: 'androidx.navigation.safeargs'
導航到目的地
使用NavController
來發(fā)起頁面跳轉(zhuǎn),可以通過以下方法獲取NavController:
NavHostFragment.findNavController(Fragment)Navigation.findNavController(Activity, @IdRes int viewId)Navigation.findNavController(View)
獲取到NavController后,就可以通過它的navigate()方法發(fā)起頁面跳轉(zhuǎn),navigate()接受action id 或 fragment id 以及導航選項及Bundle參數(shù)等作為參數(shù)。
創(chuàng)建導航選項:
val options = NavOptions.Builder()
.setEnterAnim(R.anim.slide_in_right)
.setExitAnim(R.anim.slide_out_left)
.setPopEnterAnim(R.anim.slide_in_left)
.setPopExitAnim(R.anim.slide_out_right)
.build()
通過指定的action來跳轉(zhuǎn)頁面:
Navigation.findNavController(view).navigate(R.id.viewTransactionsAction);
同一個導航圖里可以有多個相同id的action。
創(chuàng)建一個跳轉(zhuǎn)的OnClickListener:
button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null));
綁定目的跳轉(zhuǎn)和 Menu Item
要快捷的綁定Menu Item跳轉(zhuǎn)和指定的頁面,要保證目的地fragment id 和 item id 一致
// 導航圖中的目的地
<fragment android:id="@+id/details_page_fragment"
android:label="@string/details"
android:name="com.example.android.myapp.DetailsFragment" />
// 目錄項
<item
android:id="@id/details_page_fragment"
android:icon="@drawable/ic_details"
android:title="@string/details" />
// 溢出菜單目錄項
<item
android:id="@id/details_page_fragment"
android:icon="@drawable/ic_details"
android:title="@string/details"
android:menuCategory:"secondary" />
綁定NavigationView跳轉(zhuǎn)
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
NavigationUI.setupWithNavController(navigationView, navController);
其中navigationView 可以是NavigationView、BottomNavigationView等
綁定Menu Item跳轉(zhuǎn):
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Have the NavHelper look for an action or destination matching the menu
// item id and navigate there if found.
// Otherwise, bubble up to the parent.
return NavigationUI.onNavDestinationSelected(item,
Navigation.findNavController(this, R.id.my_nav_host_fragment))
|| super.onOptionsItemSelected(item)
}
在目的地之前傳輸數(shù)據(jù)
先在導航圖中創(chuàng)建要接收參數(shù),然后在代碼中用Bundle傳數(shù)據(jù):
Bundle bundle = new Bundle();
bundle.putString("amount", amount);
Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);
獲取參數(shù):
TextView tv = view.findViewById(R.id.textViewAmount);
tv.setText(getArguments().getString("amount"));
使用類型安全插件傳送參數(shù),這里假設(shè)你要從叫SpecifyAmountFragment跳轉(zhuǎn)ConfirmationFragment并傳送數(shù)據(jù),同時跳轉(zhuǎn)的action id為confirmationAction:
@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);
}
獲取參數(shù):
@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 + "")
}
將目的地組合到一個嵌套的導航圖中
按住Shift點擊多個目的地,在他們上面點擊右鍵,選擇Move to Nested Graph > New Graph
為目標分配深層鏈接
在導航圖編輯器中選中目的地后在Attributes編輯器中添加,或者在xml文件中對應(yīng)Fragment下添加:
<deepLink app:uri="https://cashdog.com/sendmoney"/>