嵌套導航圖
可以將一系列目的地歸入父級導航圖(稱為"根圖")內的一個嵌套圖中. 嵌套圖對于整理和重復使用應用界面的各個部分非常有用.
嵌套圖可以封裝其目的地,與根圖一樣,嵌套圖必須具有標識為起始目的地的目的地,嵌套圖之外的目的地(例如根圖上 目的地)只能通過其起始目的地訪問嵌套圖

如這幅圖所示,我們可以將fragmentChoose和fragmentSpecify封裝成一個Navigation,通過include引入根圖中
<!-- (root) nav_graph.xml -->
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/nav_graph"
app:startDestination="@id/fragment">
<include app:graph="@navigation/included_graph" />
<fragment
android:id="@+id/fragment"
android:name="com.example.myapplication.BlankFragment"
android:label="Fragment in Root Graph"
tools:layout="@layout/fragment_blank">
<action
android:id="@+id/action_fragment_to_second_graph"
app:destination="@id/second_graph" />
</fragment>
...
</navigation>
切記的是: inclued_graph.xml必須有app:startDestination起始目的地,
<!-- included_graph.xml -->
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/second_graph"
app:startDestination="@id/includedStart">
<fragment
android:id="@+id/includedStart"
android:name="com.example.myapplication.IncludedStart"
android:label="fragment_included_start"
tools:layout="@layout/fragment_included_start" />
</navigation>
這樣通過include引入,可以我們方便管理和達到復用的效果.
全域操作
您可以使用全域操作來創建可由多個目的地共用的通用操作,例如,你可能想要不同目的地中的多個按鈕導航到同一應用主螢屏
在 Navigation Editor 中,全域操作由一個指向相關聯目的地的小箭頭表示,如圖 所示,

創建全域操作
如需創建全域操作,請執行以下操作:
- 在 Graph Editor 中,點擊一個目的地,使其突出顯示,
- 右鍵點擊該目的地,以顯示背景關系選單,
- 依次選擇 Add Action > Global,此時系統會在該目的地左側顯示一個箭頭 (),
- 點擊 Text 標簽頁,以轉到 XML 文本視圖,全域操作的 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"
android:id="@+id/main_nav"
app:startDestination="@id/mainFragment">
...
<action android:id="@+id/action_global_mainFragment"
app:destination="@id/mainFragment"/>
</navigation>
使用全域操作
如需在代碼中使用某個全域操作,請將該全域操作的資源 ID 傳遞到每個界面元素的 navigate() 方法,如以下示例所示:
viewTransactionsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Navigation.findNavController(view).navigate(R.id.action_global_mainFragment);
}
});
這樣,無論你在哪個Fragment, 使用全域資源ID,都可以跳轉到mainFragment.
當然,這些Fragment必須屬于同一個導航圖,
轉到目的地
使用ID導航
navigate(int) 接受操作或目的地的資源 ID 作為引數,以下代碼段展示了如何導航到 ViewTransactionsFragment
viewTransactionsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Navigation.findNavController(view).navigate(R.id.viewTransactionsAction);
}
});
需要注意一點的是: ViewTransactionsFragment必須要在Navigation中進行注冊
對于按鈕,您還可以使用 Navigation 類的 createNavigateOnClickListener() 便捷方法導航到目的地,如下例所示:
button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null));
NavigationUI更新頁面組件
NavigationalUI介紹
通常來說,在頁面的切換程序中,通常還伴隨著App bar中menu選單的變化,對于不同的頁面,App bar中的menu選單很可能是不一樣的,例如,當ActionBar左邊的回傳按鈕被單擊時,我們需要回應該事件,回傳到上一個頁面,既然Navigation和App bar都需要處理頁面切換事件,那么,為了方便管理,Jetpack引入了NavigationUi 組件,使App bar中的按鈕和選單能夠與導航圖中的頁面關聯起來.
NavigationalUI基本使用
假設我們有兩個頁面,MainFragment和SettingsFragment.這兩個Fragment同屬于MainActivity. MainFragment的ActionBar右邊有一個按鈕,通過該按鈕,可以跳轉到SettingsFragment.而在SettingsFragment的ActionBar左側有一個回傳按鈕,通過該按鈕,可以回傳MainFragment.

配置nav.graph.xml
專案的導航圖檔案nav_graph.xml,可以清晰地看到頁面間的關系,MainActivity包好了MainFragment和SettingsFragment.默認加載的是MainFragment.代碼如下所示
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/nav_graph"
app:startDestination="@id/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="com.example.jetpack.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main" >
</fragment>
<fragment
android:id="@+id/settingFragment"
android:name="com.example.jetpack.settingFragment"
android:label="fragment_setting"
tools:layout="@layout/fragment_setting" />
</navigation>
menu檔案
在menu_settings.xml檔案中,為ActionBar添加選單,需要注意的是,的id與導航圖中SettingsFragment的id是一致的,這表示,當該被單擊時,將會跳轉到id所對應的Fragments.即SettingsFragment, 代碼如下所示
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/settingFragment"
android:icon="@drawable/ic_launcher_foreground"
android:title="設定界面"
/>
</menu>
MainActivity中實體化選單
AppBarConfiguration
NavigationUI 使用 AppBarConfiguration 物件管理在應用顯示區域左上角的導航按鈕行為,導航按鈕的行為會根據用戶是否位于頂級目的地而變化,
默認情況下,應用的起始目的地是唯一的頂級目的地,當用戶位于頂級目的地時,導航按鈕會變為抽屜式導航欄圖示 ,當用戶位于任何其他目的地上時,導航按鈕會顯示為向上按鈕(回傳按鈕).
簡單來說
AppBarConfiguration appBarConfiguration =
new AppBarConfiguration.Builder(navController.getGraph()).build();
當構建AppBarConfiguration時,傳入的是相對應的導航圖,那么導航圖中起始目的地就會顯示抽屜式導航欄圖示,導航圖其余的Fragment就會顯示向上按鈕,如圖所示:

這是導航圖中的起始目的地,就會沒有向上按鈕,

這是導航圖中的其余Fragment,就會默認顯示向上按鈕,
在某些情況下,如果想指定多個頂級目的地,對于這樣的情況,您可以改為將一組目的地 ID 傳遞給建構式,代碼如下所示:
appBarConfiguration = new AppBarConfiguration.Builder(R.id.mainFragment,R.id.settingFragment).build();

這樣settingFragment是沒有了向上按鈕,成為了頂級目的地
在MainActivity中實體化選單,代碼如下所示
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_settings,menu);
return true;
}
我們該如何在代碼中 回應選單單擊事件呢? 在沒有NavigationUI組件之前,我們需要判斷被單擊的選單項,接著撰寫相應的代碼,跳轉到對應的頁面,現在有了 NavigationUI組件,可以自動幫我們處理好跳轉邏輯
public class MainActivity extends AppCompatActivity {
private AppBarConfiguration appBarConfiguration;
private NavController navController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//獲取navController,通過MainActivity空白的NavHostFragment獲取
navController = Navigation.findNavController(this,R.id.nav_host_fragment);
//將導航圖和AppBarConfiguration關聯起來
appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
//將AppBarConfiguration和NavController系結起來
NavigationUI.setupActionBarWithNavController(this,navController,appBarConfiguration);
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
return NavigationUI.onNavDestinationSelected(item,navController)||super.onOptionsItemSelected(item);
}
@Override
public boolean onSupportNavigateUp() {
return NavigationUI.navigateUp(navController,appBarConfiguration)||super.onSupportNavigateUp();
}
}
我們可以通過MainActivity空白的NavHostFragment獲取navController 物件,并且通過navController獲取導航圖 和AppbarConfiguration關聯起來.
//獲取navController,通過MainActivity空白的NavHostFragment獲取
navController = Navigation.findNavController(this,R.id.nav_host_fragment);
//將導航圖和AppBarConfiguration關聯起來
appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
由于在導航圖和選單的布局檔案中,我們為SettingsFragment和menu設定了相同的Id,因此,在onOptionsItemSelected()方法中,通過NavigationUI便可以自動完成頁面跳轉
同樣,覆寫onSupportNavigateUp()方法,當我們在SettingsFragment中單擊ActionBar左邊的回傳按鈕時,NavigationUI可以幫助我們從SettingsFragment回到MainFragment,
AppBarConfiguration用于App bar的配置,NavController用于頁面的導航和切換,再通過下面這行代碼,將App bar和NavController系結起來,
NavigationUI.setupActionBarWithNavController(this,navController,appBarConfiguration);
需要注意的是,App bar是在MainActivity中進行管理的,當從MainFragment跳轉到SettingsFragment時,需要在SettingsFragment中覆寫onCreateOptionsMenu()方法,并在該方法中清除MainFragment所對應的menu
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
menu.clear();
super.onCreateOptionsMenu(menu, inflater);
}
大家一定有一個疑問,為什么需要清除呢?
因為MainFragment和SettingsFragment都是在MainActivtiy中進行顯示,在MainActivity配置了App bar, MainFragment和SettingsFragment切換只是在NavHostFragment中進行顯示,NavHostFragment 相對來說是屬于MainActivity的一個組件,并不會改變Appbar,所以我們需要在SettingsFragment清除menu;
頁面切換監聽
我們可以利用NavController提供的一個名為OnDestinationChangedListener的介面,對頁面切換事件進行監聽
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
@SuppressLint("RestrictedApi")
@Override
public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
// Log.i("true",controller.toString());
Log.i("true",destination.getNavigatorName()+destination.getDisplayName()+destination.getId());
if(destination.getId() == R.id.settingsFragment) {
toolbar.setVisibility(View.GONE);
bottomNavigationView.setVisibility(View.GONE);
} else {
toolbar.setVisibility(View.VISIBLE);
bottomNavigationView.setVisibility(View.VISIBLE);
}
}
});
通過destination.getId 可以獲取當前的fragment的id,就可以根據destination.getId判斷到了哪個目的地,對該目的地的App bar可以進行切換了,達到不同的Fragment,App bar不一樣的效果. 代碼示例如下
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
@SuppressLint("RestrictedApi")
@Override
public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
// Log.i("true",controller.toString());
Log.i("true",destination.getNavigatorName()+destination.getDisplayName()+destination.getId());
if(destination.getId() == R.id.settingFragment){
getSupportActionBar().setTitle("小鑫啊");
}else if(destination.getId() == R.id.mainFragment){
getSupportActionBar().setTitle("主頁啊");
}
}
});

當切換到settingsFragment時,標題變成了小鑫啊,到達了修改App bar的效果,也可以在切換頁面的時候,更改App bar的樣式.
好了,基本的Navigation的使用就說到這里了,不足之處,望大家見諒,謝謝,
接下來,我們就來說說,Navigation支持的另外兩種App bar和另外兩種選單
- Toolbar
- CollapsingToolbarLayout
- App bar 左側的抽屜選單(DrawLayout+NavigationView)
- 底部選單(BottomNavigation).
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/205989.html
標籤:其他
