Android架構(gòu)組件-Navigation的使用(一)
Android架構(gòu)組件-Navigation的使用(二)
在 Google I/O 2018 上新出現(xiàn)了一個(gè)導(dǎo)航組件(Navigation Architecture Component)瞳步,導(dǎo)航組件類似iOS開(kāi)發(fā)里的StoryBoard适荣,可以可視化的編輯App頁(yè)面的導(dǎo)航關(guān)系阵翎。
官方文檔:The Navigation Architecture Component
官方教程:Navigation Codelab
學(xué)習(xí)Demo:navigation
Google實(shí)驗(yàn)室的Demo: android-navigation
導(dǎo)航(Navigation)規(guī)則
- App需要有確定的起始點(diǎn)
- 使用一個(gè)棧來(lái)代表App的導(dǎo)航狀態(tài)
- 向上按鈕從不會(huì)退出你的App
- 在App任務(wù)中向上和返回按鈕是等價(jià)的
- 深度鏈接到目標(biāo)或?qū)Ш降较嗤哪繕?biāo)應(yīng)產(chǎn)生相同的堆棧
Navigation的使用
Navigation 是 Android Studio 3.2 才有的功能,所以要先下載 Android Studio 3.2, 目前 Android Studio 3.2 是預(yù)覽版东囚,正式版目前是 3.1.3跺嗽,Android studio3.2/3.3下載頁(yè)面
下載完 Android Studio 3.2 /3.3 后打開(kāi)項(xiàng)目
在 app 下的 build.gradle 導(dǎo)入 Navigation:
dependencies {
//...
implementation "android.arch.navigation:navigation-fragment-ktx:1.0.0-alpha04"
implementation "android.arch.navigation:navigation-ui-ktx:1.0.0-alpha04"
}
建立個(gè)Activity,需要用到 NavHost 來(lái)托管 Navigation页藻,NavHost 是個(gè)接口桨嫁,默認(rèn)是用 NavHostFragment 來(lái)托管,NavHostFragment 是實(shí)現(xiàn)了 NavHost 接口的份帐,查看 NavHostFragment 會(huì)看到璃吧,在注釋里他已經(jīng)提供了簡(jiǎn)單的activity布局寫(xiě)法
/**
* NavHostFragment provides an area within your layout for self-contained navigation to occur.
*
* <p>NavHostFragment is intended to be used as the content area within a layout resource
* defining your app's chrome around it, e.g.:</p>
*
* <pre class="prettyprint">
* <android.support.v4.widget.DrawerLayout
* xmlns:android="http://schemas.android.com/apk/res/android"
* xmlns:app="http://schemas.android.com/apk/res-auto"
* android:layout_width="match_parent"
* android:layout_height="match_parent">
* <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="@xml/nav_sample"
* app:defaultNavHost="true" />
* <android.support.design.widget.NavigationView
* android:layout_width="wrap_content"
* android:layout_height="match_parent"
* android:layout_gravity="start"/>
* </android.support.v4.widget.DrawerLayout>
* </pre>
*
* <p>Each NavHostFragment has a {@link NavController} that defines valid navigation within
* the navigation host. This includes the {@link NavGraph navigation graph} as well as navigation
* state such as current location and back stack that will be saved and restored along with the
* NavHostFragment itself.</p>
*
* <p>NavHostFragments register their navigation controller at the root of their view subtree
* such that any descendant can obtain the controller instance through the {@link Navigation}
* helper class's methods such as {@link Navigation#findNavController(View)}. View event listener
* implementations such as {@link android.view.View.OnClickListener} within navigation destination
* fragments can use these helpers to navigate based on user interaction without creating a tight
* coupling to the navigation host.</p>
*/
public class NavHostFragment extends Fragment implements NavHost {
//...
}
參考例子,我們的NavigationMainActivity布局:
<layout 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.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
<fragment
android:id="@+id/garden_nav_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_garden"/>
</LinearLayout>
<android.support.design.widget.NavigationView
android:id="@+id/navigation_view"
style="@style/Widget.MaterialComponents.NavigationView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header"
app:menu="@menu/menu_navigation"/>
</android.support.v4.widget.DrawerLayout>
</layout>
AppBarLayout和Toolbar暫不做介紹
fragment會(huì)發(fā)現(xiàn)有2分屬性:
app:navGraph: 屬性賦值的是 nagation 文件
app:defaultNavHost: 這個(gè)是和返回鍵相關(guān)的
這個(gè)nagation文件是什么呢废境?我們先來(lái)建Fragment和activity:
Menu1Fragment:
class Menu1Fragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_menu1, container, false)
}
}
xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".navigation.Menu1Fragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Menu1Fragment" />
</FrameLayout>
Menu2Fragment:
class Menu2Fragment : Fragment() {
lateinit var binding: FragmentMenu2Binding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_menu2, container, false)
return binding.root
}
}
xml:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".navigation.Menu2Fragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Menu2Fragment" />
<android.support.v7.widget.AppCompatButton
android:id="@+id/btn_to_second_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_10dp"
android:text="去第二個(gè)頁(yè)面"
android:textAllCaps="false"
/>
</LinearLayout>
</FrameLayout>
</layout>
Menu2NextFragment此處先省略不寫(xiě)了畜挨。
在res目錄右鍵選擇new -> Android Resource File
新建個(gè)navigation資源文件:
在res目錄下會(huì)產(chǎn)生navigation文件夾:
會(huì)產(chǎn)生這樣的文件:
<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_garden">
</navigation>
寫(xiě)<左尖括號(hào)的時(shí)候,會(huì)提示:
然后我們將fragment添加進(jìn)去:
id: 就像寫(xiě)布局的 id 那樣需要給個(gè) id 才能找到它
name: 哪個(gè) Fragment 類名
tools:layout: fragment的layout
就像下面寫(xiě)好的這樣:
navigation -> nav_garden:
<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"
app:startDestination="@+id/menu1_fragment"
android:id="@+id/nav_garden">
<fragment
android:id="@+id/menu1_fragment"
android:name="com.ghp.demo.databindingdemoproject.navigation.Menu1Fragment"
android:label="@string/menu1_title"
tools:layout="@layout/fragment_menu1"/>
<fragment
android:id="@+id/menu2_fragment"
android:name="com.ghp.demo.databindingdemoproject.navigation.Menu2Fragment"
android:label="@string/menu2_title"
tools:layout="@layout/fragment_menu2">
</fragment>
<fragment
android:id="@+id/menu2_next_fragment"
android:name="com.ghp.demo.databindingdemoproject.navigation.Menu2NextFragment"
android:label="@string/menu2next_title"
tools:layout="@layout/fragment_menu2_next"/>
</navigation>
仔細(xì)查看代碼的話噩凹,會(huì)發(fā)現(xiàn)在navigation下有個(gè)app:startDestination巴元,這是給導(dǎo)航指定起始位置的,必須要設(shè)置驮宴,不然會(huì)奔潰報(bào)錯(cuò)务冕。
點(diǎn)擊下面的Design查看下:
這樣我們的app:navGraph="@navigation/nav_garden"就創(chuàng)建好了
結(jié)合ToolBar和navigationView
下面新建個(gè)menu文件:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<!--id 對(duì)應(yīng)navigation的id-->
<item
android:id="@id/menu1_fragment"
android:title="@string/menu1_title"/>
<item
android:id="@id/menu2_fragment"
android:title="@string/menu2_title"/>
</menu>
這樣NavigationView 的app:menu="@menu/menu_navigation"也創(chuàng)建好了。注意menu這里的id需要和navigation的id對(duì)應(yīng)幻赚。
activity代碼修改為:
class NavigationMainActivity : AppCompatActivity() {
lateinit var binding: ActivityNavigationMainBinding
lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_navigation_main)
// Set up ActionBar
setSupportActionBar(binding.toolbar)
navController = Navigation.findNavController(this, R.id.garden_nav_fragment)
NavigationUI.setupActionBarWithNavController(this, navController, binding.drawerLayout)
// Set up navigation menu
binding?.navigationView.setupWithNavController(navController)
}
override fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(binding.drawerLayout, navController)
}
override fun onBackPressed() {
if(binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.drawerLayout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
}
Navigation 可以和 Toolbar 相結(jié)合,Toolbar 左邊會(huì)出現(xiàn)個(gè)返回的箭頭臊旭,這樣箭頭的顯示和隱藏控制都不用我們?nèi)?xiě)了落恼。
用 Toolbar 的話 Activity 的 style 要設(shè)置 NoActionBar 的。
這里用到了 NavigationUI 的setupActionBarWithNavController(AppCompatActivity activity, NavController navController) 方法离熏,還覆蓋了 onSupportNavigateUp() 方法佳谦。是因?yàn)樵谒拗鱝ctivity里需要重寫(xiě)onSupportNavigateUp方法去啟動(dòng)fragment。想了解更多的話滋戳,可以進(jìn)入源碼查看都做了什么
界面間跳轉(zhuǎn)
看上圖右側(cè)钻蔑,可以添加Arguments(傳參),Action(頁(yè)面間跳轉(zhuǎn)),我們?cè)贛enu2Fragment上添加跳轉(zhuǎn)到Menu2NextFragment:
<fragment
android:id="@+id/menu2_fragment"
android:name="com.ghp.demo.databindingdemoproject.navigation.Menu2Fragment"
android:label="@string/menu2_title"
tools:layout="@layout/fragment_menu2">
<action android:id="@+id/action_livedata_fragment_to_livedata2_fragment"
app:destination="@id/menu2_next_fragment"
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>
action的id 和 destination:
id 就是這個(gè) action 的 id,
destination 是目的地奸鸯,要跳轉(zhuǎn)到哪里的
還可以設(shè)置動(dòng)畫(huà)
查看design:
Menu2Fragment和Menu2NextFragment之間有根帶箭頭的線咪笑,右側(cè)Action的位置有跳轉(zhuǎn)id。
要跳轉(zhuǎn)到第二個(gè) Fragment 得使用
NavController
來(lái)發(fā)起頁(yè)面跳轉(zhuǎn)娄涩,可以通過(guò)以下方法獲取NavController
:
NavHostFragment.findNavController(Fragment)
Navigation.findNavController(Activity, @IdRes int viewId)
Navigation.findNavController(View)
獲取到NavController
后窗怒,就可以通過(guò)它的navigate()
方法發(fā)起頁(yè)面跳轉(zhuǎn),navigate()
接受action id 或 fragment id 以及導(dǎo)航選項(xiàng)及Bundle參數(shù)等作為參數(shù)。
/**
* Find a {@link NavController} given the id of a View and its containing
* {@link Activity}. This is a convenience wrapper around {@link #findNavController(View)}.
*
* <p>This method will locate the {@link NavController} associated with this view.
* This is automatically populated for the id of a {@link NavHost} and its children.</p>
*
* @param activity The Activity hosting the view
* @param viewId The id of the view to search from
* @return the {@link NavController} associated with the view referenced by id
* @throws IllegalStateException if the given viewId does not correspond with a
* {@link NavHost} or is not within a NavHost.
*/
@NonNull
public static NavController findNavController(@NonNull Activity activity, @IdRes int viewId) {
View view = ActivityCompat.requireViewById(activity, viewId);
NavController navController = findViewNavController(view);
if (navController == null) {
throw new IllegalStateException("Activity " + activity
+ " does not have a NavController set on " + viewId);
}
return navController;
}
/**
* Find a {@link NavController} given a local {@link View}.
*
* <p>This method will locate the {@link NavController} associated with this view.
* This is automatically populated for views that are managed by a {@link NavHost}
* and is intended for use by various {@link android.view.View.OnClickListener listener}
* interfaces.</p>
*
* @param view the view to search from
* @return the locally scoped {@link NavController} to the given view
* @throws IllegalStateException if the given view does not correspond with a
* {@link NavHost} or is not within a NavHost.
*/
@NonNull
public static NavController findNavController(@NonNull View view) {
NavController navController = findViewNavController(view);
if (navController == null) {
throw new IllegalStateException("View " + view + " does not have a NavController set");
}
return navController;
}
還有一種是通過(guò) NavHostFragment 類
/**
* Find a {@link NavController} given a local {@link Fragment}.
*
* <p>This method will locate the {@link NavController} associated with this Fragment,
* looking first for a {@link NavHostFragment} along the given Fragment's parent chain.
* If a {@link NavController} is not found, this method will look for one along this
* Fragment's {@link Fragment#getView() view hierarchy} as specified by
* {@link Navigation#findNavController(View)}.</p>
*
* @param fragment the locally scoped Fragment for navigation
* @return the locally scoped {@link NavController} for navigating from this {@link Fragment}
* @throws IllegalStateException if the given Fragment does not correspond with a
* {@link NavHost} or is not within a NavHost.
*/
@NonNull
public static NavController findNavController(@NonNull Fragment fragment) {
Fragment findFragment = fragment;
while (findFragment != null) {
if (findFragment instanceof NavHostFragment) {
return ((NavHostFragment) findFragment).getNavController();
}
Fragment primaryNavFragment = findFragment.requireFragmentManager()
.getPrimaryNavigationFragment();
if (primaryNavFragment instanceof NavHostFragment) {
return ((NavHostFragment) primaryNavFragment).getNavController();
}
findFragment = findFragment.getParentFragment();
}
// Try looking for one associated with the view instead, if applicable
View view = fragment.getView();
if (view != null) {
return Navigation.findNavController(view);
}
throw new IllegalStateException("Fragment " + fragment
+ " does not have a NavController set");
}
都是 public static 的方法扬虚,所以得到 NavController 之后呢努隙,NavController 有 navigate 方法可以做跳轉(zhuǎn)的
/**
* Navigate to a destination from the current navigation graph. This supports both navigating
* via an {@link NavDestination#getAction(int) action} and directly navigating to a destination.
*
* @param resId an {@link NavDestination#getAction(int) action} id or a destination id to
* navigate to
* @param args arguments to pass to the destination
*/
public final void navigate(@IdRes int resId, @Nullable Bundle args) {
navigate(resId, args, null);
}
這里的參數(shù) resId ,從注釋中也知道是 action 的那個(gè) id。所以辜昵,給按鈕添加事件做跳轉(zhuǎn)
修改Menu2Fragment:
class Menu2Fragment : Fragment() {
lateinit var binding: FragmentMenu2Binding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater,R.layout.fragment_menu2, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.btnToSecondFragment.addClickAction {
Navigation.findNavController(view).navigate(R.id.action_livedata_fragment_to_livedata2_fragment)
}
}
}
傳遞數(shù)據(jù)
有時(shí)候可能要從第一個(gè) Fragment 帶些數(shù)據(jù)去第二個(gè) Fragment荸镊,那怎么辦,也很簡(jiǎn)單堪置,navigate 有個(gè)倆參數(shù)的方法
public final void navigate(@IdRes int resId, @Nullable Bundle args) {
navigate(resId, args, null);
}
第二個(gè)參數(shù) Bundle 是經(jīng)常用的了躬存,跳轉(zhuǎn)后 Activity 可以用 getIntent() 獲取,F(xiàn)ragment 可以通過(guò) getArguments() 獲取晋柱,修改Menu2Fragment的點(diǎn)擊事件:
binding.btnToSecondFragment.addClickAction {
var bundle: Bundle = bundleOf(
"test" to getString(R.string.menu2next_args),
"num" to 9
)
Navigation.findNavController(view).navigate(R.id.action_livedata_fragment_to_livedata2_fragment, bundle)
}
Menu2NextFragment:
class Menu2NextFragment : Fragment() {
lateinit var binding: FragmentMenu2NextBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_menu2_next, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var test: String = arguments?.getString("test")?:""
var num: Int = arguments?.getInt("num")?:0
}
}
這里是 Fragment 优构,跳轉(zhuǎn)后用 getArguments() 去獲取
類型安全的方式傳遞數(shù)據(jù)
Navigation 還提供了一種安全的數(shù)據(jù)傳遞,是怎樣的呢雁竞?先配置安全插件,在 Project 根目錄下的 build.gradle導(dǎo)入:
dependencies {
//...
classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0-alpha04"
}
在 app 下的 build.gradle 里 apply, 同步一下 gradle
apply plugin: 'androidx.navigation.safeargs'
配置完成后钦椭,我們?cè)趎av添加Arguments數(shù)據(jù)傳遞:
<fragment
android:id="@+id/menu2_fragment"
android:name="com.ghp.demo.databindingdemoproject.navigation.Menu2Fragment"
android:label="@string/menu2_title"
tools:layout="@layout/fragment_menu2">
<action android:id="@+id/action_livedata_fragment_to_livedata2_fragment"
app:destination="@id/menu2_next_fragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
<argument
android:name="test"
android:defaultValue="@string/menu2next_args"
app:argType="string"/>
<argument
android:name="num"
app:argType="integer"
android:defaultValue="0" />
</fragment>
argument 有三個(gè)屬性 name、defaultValue 和 type,
- name 就是名字到時(shí)會(huì)生成這個(gè)名字的 set 和 get 方法碑诉,
- defaultValue 是默認(rèn)值彪腔,
- type 就是數(shù)據(jù)類型,有以下幾種可以使用
怎么用呢进栽?使用也簡(jiǎn)單德挣,生成的 argument 的類使用 Builder 模式,這里的數(shù)據(jù)是從Menu2Fragment 傳數(shù)據(jù)給Menu2NextFragment快毛。
查看生成的Menu2FragmentArgs里的Builder方法:
public static class Builder {
@NonNull
private String test = "@string/menu2next_args";
private int num = 0;
public Builder(Menu2FragmentArgs original) {
this.test = original.test;
this.num = original.num;
}
public Builder() {
}
@NonNull
public Menu2FragmentArgs build() {
Menu2FragmentArgs result = new Menu2FragmentArgs();
result.test = this.test;
result.num = this.num;
return result;
}
@NonNull
public Builder setTest(@NonNull String test) {
if (test == null) {
throw new IllegalArgumentException("Argument \"test\" is marked as non-null but was passed a null value.");
}
this.test = test;
return this;
}
@NonNull
public Builder setNum(int num) {
this.num = num;
return this;
}
@NonNull
public String getTest() {
return test;
}
public int getNum() {
return num;
}
}
使用如下:
var bundle: Bundle = bundleOf(
"test" to getString(R.string.menu2next_args),
"num" to 9
)
var menu2FragmentArgs: Menu2FragmentArgs = Menu2FragmentArgs.Builder(Menu2FragmentArgs.fromBundle(bundle)).build()
Navigation.findNavController(view).navigate(R.id.action_livedata_fragment_to_livedata2_fragment, menu2FragmentArgs.toBundle())
Menu2NextFragment接收:
var menu2FragmentArgs: Menu2FragmentArgs = Menu2FragmentArgs.fromBundle(arguments)
var test: String = menu2FragmentArgs.test
var num: Int = menu2FragmentArgs.num
返回
defaultNavHost 這個(gè)屬性和返回鍵有關(guān)的格嗅,如果把這個(gè)屬性改為 false,從第一個(gè) Fragment 跳到第二個(gè) Fragment 再按返回鍵就會(huì)直接退出程序唠帝。
第二個(gè) Fragment 可以不用按返回鍵返回第一個(gè) Fragment, 通過(guò) NavController 去控制屯掖,修改下Menu2NextFragment的布局,添加個(gè)按鈕襟衰。
class Menu2NextFragment : Fragment() {
//...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//...
binding.btnBackMenu1Fragment.addClickAction {
Navigation.findNavController(view).popBackStack(R.id.menu2_fragment, false)
}
}
}
NavController 有 navigateUp() 和 popBackStack() 都可以返回上一級(jí)贴铜,有什么區(qū)別:
popBackStack() 如果當(dāng)前的返回棧是空的就會(huì)報(bào)錯(cuò),因?yàn)闂J强盏牧似偕梗琻avigateUp() 則不會(huì)绍坝,還是停留在當(dāng)前界面
看看 popBackStack() 源碼,第一句就是判斷返回棧是不是空的
public boolean popBackStack() {
if (mBackStack.isEmpty()) {
// Nothing to pop if the back stack is empty
return false;
}
// Pop just the current destination off the stack
return popBackStack(getCurrentDestination().getId(), true);
}
查看navigateUp源碼苔悦,做了判斷 返回棧是不是只剩一個(gè)轩褐,不是的話就會(huì)去調(diào)用 popBackStack()
public boolean navigateUp() {
if (mBackStack.size() == 1) {
// If there's only one entry, then we've deep linked into a specific destination
// on another task so we need to find the parent and start our task from there
NavDestination currentDestination = getCurrentDestination();
int destId = currentDestination.getId();
NavGraph parent = currentDestination.getParent();
while (parent != null) {
if (parent.getStartDestination() != destId) {
TaskStackBuilder parentIntents = new NavDeepLinkBuilder(NavController.this)
.setDestination(parent.getId())
.createTaskStackBuilder();
parentIntents.startActivities();
if (mActivity != null) {
mActivity.finish();
}
return true;
}
destId = parent.getId();
parent = parent.getParent();
}
// We're already at the startDestination of the graph so there's no 'Up' to go to
return false;
} else {
return popBackStack();
}
}
popBackStack還有個(gè)方法
public boolean popBackStack(@IdRes int destinationId, boolean inclusive) {//...}
第一個(gè)參數(shù)是 Navigation 文件的 fragment 的 id,不是 action 的间坐,
第二個(gè)參數(shù)是指是否包含第一個(gè)參數(shù) id 那個(gè)也彈出棧
動(dòng)態(tài)加載Navigation
有時(shí)候不想馬上啟動(dòng) Start Destination灾挨,或者從別的地方收到傳過(guò)來(lái)的數(shù)據(jù)邑退,然后要在 Start Destination 中用的需求,這時(shí)就不能在 layout 中寫(xiě) navGraph劳澄,因?yàn)閷?xiě)了 navGraph 一啟動(dòng)就會(huì)去加載 Start Destination地技,這時(shí)可以用代碼去動(dòng)態(tài)加載 Navigation 文件的內(nèi)容,從 NavHostFragment 入手秒拔。
- 修改下 Activity 的 layout莫矗,把 NavHostFragment 的 navGraph 屬性去掉
- 在 Activity 里加載
var navHostFragment: NavHostFragment = supportFragmentManager.findFragmentById(R.id.garden_nav_fragment) as NavHostFragment
var navSimple: NavGraph = navHostFragment.navController.navInflater.inflate(R.navigation.nav_garden)
var menu2FragDestination: NavDestination = navSimple.findNode(R.id.menu2_fragment)
var menu2FragmentArgs: Menu2FragmentArgs = Menu2FragmentArgs.fromBundle(bundleOf("test" to getString(R.string.menu2next_args), "num" to 9))
menu2FragDestination.setDefaultArguments(menu2FragmentArgs.toBundle())
navHostFragment.navController.graph = navSimple
這里先通過(guò) FragmentManager 找到 NavHostFragment,navHostFragment 有 getNavController() 方法砂缩,
NavController 里 getNavInflater() 方法獲得 NavInflater作谚,
NavInflater 這個(gè)類似 LayoutInflater, 通過(guò) inflate() 去加載 Navigation,
設(shè)置了數(shù)據(jù)后通過(guò) NavController 的 setGraph(NavGraph graph) 就加載出來(lái)了
參考文章: