Jetpack之Navigation使用及源码解读

------《Jetpack之Navigation》

  • Navigation介绍
    • Navigation使用
      • 第一步:引入依赖
      • 第二步:在Activity布局中加入Fragment视图
      • 第三步:创建navGraph设计视图XML文件
      • 第四步:创建NavController进行路由控制
    • Navigation动态加载
    • Navigation使用Deeplink跳转指定Fragment
      • 第一步:添加deepLink标签
      • 第二步:AndroidMainfest中找到Fragment所在的Activity加入标签
      • 第三步:deeplink传递参数
    • NavigationUI
    • Navigation源码解析

Navigation介绍

首先Navigation是一个架构组件,因为切换Activity是一个Binder通信的过程,所以Activity是属于比较重的组件。而Fragment的切换其实只是View的切换,比较轻量级。因此单Activity加Fragment切换成为了比较常见的架构方式。

为什么要用Navigation:1、使得Fragment的管理简单,可读性高。2、传递数据方式简单。3、标准的栈管理。4、支持Deeplink、URL Link定位到Fragment。5、处理动画更加方便。下面也主要围绕这几点进行讲解。

使用Navigation的重要三部分
Navigation Graph:用于对Fragment进行配置的配置文件,需要在res/navigation/下创建的xml文件
FragmentContainerView/NavHostFragment:一系列Fragment的容器,用于承载Fragment
NavController:用于处理Fragment路由跳转

Navigation使用

第一步:引入依赖

def nav_version = "2.5.0" //最新版本
// Java language implementation
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"

// Kotlin
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

// Feature module Support(navigation V2.3.0加入)
//支持模块化跳转时的支持包(国内基本不用,因为需要它需要Google Play Store 支持)
//优点:按需加入模块 类似于插件化
implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"

// Testing Navigation(2.3.0后加入的)
//提供一个TestNavHostController 来单独测试fragment与其NavController之间的交互(生命周期状态、调度器状态)
androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"

// Jetpack Compose Integration
//用于在使用 Jetpack Compose 构建的屏幕中实现一致而惯用的导航方式
implementation "androidx.navigation:navigation-compose:$nav_version

第二步:在Activity布局中加入Fragment视图

1、android:name=“androidx.navigation.fragment.NavHostFragment”:代表这个容器就是用来管理Fragment的容器
2、app:navGraph要导入我们控制的Fragment设计视图
3、app:defaultNavHost=“true” 代表可以拦截系统的返回键,托管给路由(不然会走系统的返回)。

<androidx.fragment.app.FragmentContainerView
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:defaultNavHost="true"  
    app:navGraph="@navigation/my_navigation"/>

第三步:创建navGraph设计视图XML文件

创建navigation文件夹 -> new xml文件 -> 选择navigation

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_navigation"
    app:startDestination="@id/OneFragment">  <!--开始的第一个界面-->
    <fragment
        android:id="@+id/TwoFragment"
        android:name="com.example.navigtion.TwoFragment"
        android:label="fragment_second"
        tools:layout="@layout/two_fragment" >   <!--Fragment对应的界面视图-->
        <argument
            android:name="content"
            app:argType="string"
            app:nullable="true"
            android:defaultValue="Three_Fragment" />  <!--传值标签-->
        <action
            android:id="@+id/action_twoFragment_to_threeFragment"
            app:destination="@id/ThreeFragment"
            app:enterAnim="@android:anim/fade_in"
            app:exitAnim="@android:anim/fade_out"
            app:popEnterAnim="@android:anim/slide_in_left"
            app:popExitAnim="@android:anim/slide_out_right" /> <!--跳转标签+动画-->
    </fragment>
</navigation>

第四步:创建NavController进行路由控制

返回控制两种:
popBackStack:可以返回上一级或者可以指定路由返回的action进行当前页面的退栈跳转
navigateUp:返回当前页面堆栈的栈顶页面(内部就是popBackStack进行实现的)

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    Button viewById = inflate.findViewById(R.id.button_view);
    viewById.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //跳转控制器
            NavController navController = Navigation.findNavController(inflate);  
            //Kotlin中直接使用view.findNavController就可以获取到 (扩展了一个函数)
            Bundle bundle = new Bundle();
            bundle.putString("content", "第一个Fragment的值");
            //传值方式很多种官方更推荐倒入Safe Args方式更加安全的传值
            navController.navigate(R.id.action_oneFragment_to_twoFragment, bundle);
            //navController.popBackStack(); //路由的栈管理(回到指定页面)
            //Navigation.findNavController(it).navigateUp()
        }
    });
}

上面的是返回控制,那么如果我想A跳转B,B跳转C后只保留C页面的话。那么就需要以下两个action标签进行配合:
popUpTo:指的是在当前路由中,一直将页面出栈,直到指定的页面为止。
popUpToInclusive:则是代表包含关系,是否包含指定的页面
举例:在到达目的地 C 之后,返回堆栈包含每个目的地(A、B 和 C)的一个实例。当返回到目的地 A 时,我们也
popUpTo A,也就是说我们会在导航过程中从堆栈中移除 B 和 C。利用
app:popUpToInclusive=“true”,我们还会将第一个 A 从堆栈上弹出,从而有效地清除它。请注意,如果您不使用
app:popUpToInclusive,则返回堆栈会包含目的地 A 的两个实例。

<fragment
    android:id="@+id/registerFragment"
    android:name="com.example.navigation.RegisterFragment"
    android:label="Register">
    <action
        android:id="@+id/action_registerFragment_to_mainListFragment"
        app:destination="@id/mainListFragment"
        app:popUpTo="@id/loginFragment"
        app:popUpToInclusive="true" />
</fragment>

Navigation动态加载

实际上和动态Inflate布局再添加布局到容器的场景非常类似,Navigation动态加载也是将navGraph从xml中创建好之后设置给navigation。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //动态加载
    FragmentManager supportFragmentManager = getSupportFragmentManager();
    NavHostFragment navHostFragment = (NavHostFragment) supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
    NavController navController = navHostFragment.getNavController();
    navController.setGraph(R.navigation.my_navigation);
}

Navigation使用Deeplink跳转指定Fragment

第一步:添加deepLink标签

<!--超链接:<a href="http://aa.bb.com/frag2"> -->
<Fragment>
...
    <deepLink
        android:id="@+id/deepLink"
        android:autoVerify="true"
        app:uri="http://aa.bb.com/frag2"
    />  
    <!--注意如果不加frag2(路径)的话只会跳转到导航图的开始页-->
    <!--所以在跳转的deepLink也要加上/frag2(路径)-->
</Fragment>

第二步:AndroidMainfest中找到Fragment所在的Activity加入标签

<nav-graph android:value="@navigation/my_navigation"/>

注意:deepLink跳转会冲新走你Fragment导航图的路径。
例:导航图A -> 导航图B -> 导航图C ->导航图A。 你跳转到FragmentB。实际上是先跳转FragmentA再到FragmentB。所以此时你按返回键会回到FragmentA。

第三步:deeplink传递参数

超链接:<a href="http://aa.bb.com/tom/24"> <!--name:tom  age:24-->
<Fragment>
...
     <argument
            android:name="name"
            app:argType="string"
            app:nullable="true"/>  <!--传值标签-->
     <argument
            android:name="age"
            app:argType="string"
            app:nullable="true"/>  <!--传值标签-->
    <deepLink
        android:id="@+id/deepLink"
        android:autoVerify="true"
        app:uri="http://aa.bb.com/frag2/{name}/{age}"
    />  
    <!--注意如果不加frag2(路径)的话只会跳转到导航图的开始页-->
    <!--所以在跳转的deepLink也要加上/frag2(路径)-->
</Fragment>

还可以使用NavDeepLinkRequest创建deepLink链接在应用内跳转。主要注意的是在应用内使用时只能导航到NavHost导航图以内的目的地,或者可以通过include将其他模块成为你目前导航图的子视图。

NavigationUI

Fragment的切换,除了Fragment页面本身的切换,通常还伴有App bar的变化。为了方便统一管理,Navigation组件引入了NavigationUI类。帮助我们处理App bar的切换逻辑。
NavigationUI对三种类型的App bar提供了支持:

  • Toolbar
  • ActionBar
  • CollapsingToolbarLayout
    绑定方式:既然Fragment是我们的NavController进行管理的那么我们只需要将我们的App bar与我们的NavController进行绑定就可以了。
    只需要以下三步就可以达到我们的目的:
    1、获取App bar配置:AppBarConfiguration
AppBarConfiguration appBarConfiguration = 
    new AppBarConfiguration.Builder(navController.getGraph()).build();

2、将NavController和AppBarConfiguration进行绑定

NavigationUI.setupActionBarWithNavController(this, 
                                    navController, appBarConfiguration);

3、将需要交互的App barUI与NavController和AppBarConfiguration进行绑定

NavigationUI.setupWithNavController(toolbar, 
                                    navController,appBarConfiguration);

所有的NavigationUI绑定都是如此:下面示例代码是与BottomNavigationView的绑定

//获取BottomNavigationView
BottomNavigationView bottomNavigationView = findViewById(R.id.nav_bottom_view);
//获取AppBar配置AppBarConfiguration
AppBarConfiguration appBarConfig = 
        new AppBarConfiguration.Builder(R.id.OneFragment, R.id.TwoFragment,
        R.id.ThreeFragment).build();
//进行绑定
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfig);
NavigationUI.setupWithNavController(bottomNavigationView, navController);

并且NavigationUI为我们提供了切换的监听OnDestinationChangedListener( )对Destination切换事件进行监听。

navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener()
{
    @Override
    public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments)
    {
        Log.e(">>>>>", "onDestinationChanged: " + destination.getDisplayName());
    }
});

Navigation源码解析

按照我们使用的顺序去解析源码
初始化及启动第一个Fragment的过程
1、首先是在我们的布局中添加容器NavHostFragment,并设置导航navGraph。
Jetpack之Navigation使用及源码解读_第1张图片
2、解析NavGraph及跳转前夕
Jetpack之Navigation使用及源码解读_第2张图片
3、跳转
Jetpack之Navigation使用及源码解读_第3张图片
通过navigate进行页面跳转以及栈的管理(FragmentA 跳转至 FragmentB)
Jetpack之Navigation使用及源码解读_第4张图片
(FragmentB返回FragmentA)
Jetpack之Navigation使用及源码解读_第5张图片

你可能感兴趣的:(Android,jetpack,android,android,jetpack)