简介
导航是指支持用户导航、进入和退出应用中不同内容片段的交互。Android Jetpack 的导航组件可帮助您实现导航,无论是简单的按钮点击,还是应用栏和抽屉式导航栏等更为复杂的模式,该组件均可应对。其实最最重要的是,使页面跳转管理变得非常方便。底层原理还是FragmentManager管理,Navigation组件封装了一层。
导航组件由以下三个关键部分组成:
- 导航图:在一个集中位置包含所有导航相关信息的 XML 资源。这包括应用内所有单个内容区域(称为目标)以及用户可以通过应用获取的可能路径。
NavHost
:显示导航图中目标的空白容器。导航组件包含一个默认NavHost
实现 (NavHostFragment
),可显示 Fragment 目标。NavController
:在NavHost
中管理应用导航的对象。当用户在整个应用中移动时,NavController
会安排NavHost
中目标内容的交换。
对于刚刚接触Navigation组件,我们常常明白的点主要有三点:
-
怎么找到NavController
-
怎么跳转
-
xml里写是啥
怎么找到NavController
说明之前,首先上xml
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_home_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:tag="navi_host_tag"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/user_navi" />
从FragmentContainerView中找到NaviController:
1 通过NavHostFragment本身
Fragment navHostFragment = getSupportFragmentManager().findFragmentById(binding.navHomeFragment.getId());
//或者navHostFragment = getSupportFragmentManager().findFragmentByTag(“navi_host_tag”);
if(navHostFragment instanceof NavHostFragment){
NavHostFragment navHost = (NavHostFragment) this.navHostFragment;
NavController navController = navHost.getNavController();
navController.navigate(R.id.register);
}
2 因为NaviController是通过view.setTag(key,value)记录下来的,所以也可以
Object tag = binding.navHomeFragment.getTag(R.id.nav_controller_view_tag);
if(tag instanceof NavController){
Log.d(TAG, "onResume: ");
}
3 直接使用Navigation.findNavController(id)
NavController navController = Navigation.findNavController(this, binding.navHomeFragment.getId());
4 直接使用Navigation.findNavController(view)
NavController navController = Navigation.findNavController(binding.navHomeFragment);
//或者
NavController navController = Navigation.findNavController(binding.navHomeFragment.someChildView);
这里要说明一下,
方法1 原理是NavHostFragment实现了NavHost,所以可以直接getController
方法2 3 4 NaviController是通过view.setTag(key,value)记录下来的,其实Navigation.find的方法都是通过tag,往parent方向查找,所以通过子View或者id都可以找到导航图的Controller
导航图的定义
下面是一个模板
<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/title_screen">
...
<fragment
android:id="@+id/register"
android:name="com.example.android.navigationsample.Register"
android:label="fragment_register"
tools:layout="@layout/fragment_register">
<action
android:id="@+id/action_register_to_match"
app:destination="@id/match"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"/>
<action
...
/>
<argument android:name="userName"
android:defaultValue="name"/>
<deepLink app:uri="www.example.com/user/{userName}" />
</fragment>
<fragment>
...
</fragment>
...
</navigation>
其中action标签的含义如下
<action
app:destination="@id/leaderboard" //导航到目的地
app:popUpTo="@id/title_screen"//先弹出到哪里,这个动作在目的地前
app:popUpToInclusive="true"//是否也弹出控制的本身也弹出
app:popEnterAnim="@anim/slide_in_left"//弹出时进入动作
app:popExitAnim="@anim/slide_out_right"//弹出时退出动作
app:enterAnim="@anim/slide_in_left"//进入动作
app:exitAnim="@anim/slide_out_right"//退出动作
/>
fragment中的数据传输
<argument android:name="userName"
android:defaultValue="name"/>
deepLink 的deeplink
<deepLink app:uri="www.example.com/user/{userName}" />
深连接要在AndroidManifest.xml中activity标签中添加nav-graph
<activity android:name="com.example.android.navigationsample.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- This is added to handle a deep link -->
<nav-graph android:value="@navigation/navigation" />
</activity>
怎么跳转
这个主要有navigation(resId)、navigateUp()、popBackStack(destinationId, inclusive)
1 **navigation(resId)**这个方法最常用,其中resId既可以是navigation.xml里的fragment的resId也可以是action的resId。action标签的resId可以很丰富。
另外,navigation(…)还可以指定很多种类的参数,可以根据需要使用。
对于以下标签
<fragment
android:id="@+id/login_in"
android:name="com.lqbs.gyso.view.LoginFragment"
android:label="fragment_login"
tools:layout="@layout/fragment_login" >
<action
android:id="@+id/nav_go_register"
app:destination="@+id/register"/>
</fragment>
可以
NavController navController = Navigation.findNavController(binding.navHomeFragment);
navController.navigate(R.id.nav_go_register);
或
NavController navController = Navigation.findNavController(binding.navHomeFragment);
navController.navigate(R.id.login_in);
其他的navigation函数如下
2 **popBackStack(destinationId, inclusive)**出栈到指定id,inclusive如果是true则destinationId这个对象也弹出,也就是弹出到destinationId的前一个。通过下面的操作可以返回到打开登录界面的界面
NavController navController = Navigation.findNavController(binding.navHomeFragment);
navController.popBackStack(R.id.login_in, true);
源码中可以看到,直接从栈中删除了
NavBackStackEntry entry = mBackStack.removeLast();
3 **navigateUp()**可以说的我们常见的返回,与popBackStack很相似,但是navigateUp处理了深连接。即处理了与其他应用之间的跳转连接。一般在返回键一般使用navigateUp而不用popBackStack
NavController navController = Navigation.findNavController(binding.navHomeFragment);
navController.navigateUp()
总结
Navigation组件就是为了方便我们管理我们的界面跳转的,有三个工作要做,找到NaviController, 定义跳转的属性,跳转。跳转往前,我们指定目的地;返回,我们指定返回到哪。