Navigation

Android工程模板Bottom Navigation Activity分析

  1. 首先观察生成工程的AndroidManifest文件,其中只有一个Activity。
  2. 运行程序,可以看到底部是三个导航栏,上面是内容区域,点击不同的导航栏tab就显示不同的内容。
  3. 分析入口Activity,查看布局文件。
    1. 底部导航栏通过BottomNavigationView实现,具体的导航栏按钮通过app:menu来指定menu资源。
    2. 内容区域是通过静态使用fragment来实现的,通过android:name指定具体的Fragment是androidx.navigation.fragment.NavHostFragment;除此以外,还有app:defaultNavHost和app:navGraph属性比较重要。

app:defaultNavHost是用来表示Fragment是否可以拦截系统返回键;app:navGraph指定navigation资源,这个资源能够指定页面间的路由结构(也就是跳转方式)。在这个navigation资源文件中,其根标签是navigation,根标签中的app:startDestination属性指定默认显示的fragment页面;子标签就是一个个不同的静态fragment,这些fragment中可以添加argument、action、deepLink子标签,其中argument子标签表示跳转到当前fragment所需要的参数;action表示当前页跳转至目标页的动作,它是通过app:destination属性指定跳转的fragment的id来连接不同的destination的;deepLink通过指定app:uri的方式拉起当前页面。

NavHostFragment分析

以下代码的分析会涉及到NavGraphNavigator、ActivityNavigator、DialogFragmentNavigator、FragmentNavigator,这四个类都继承Navigator。Navigator是一个抽象类,它定义了在应用中导航的机制。除此以外,Navigator需要一个继承自NavDestination的泛型,这个NavDestination代表整个导航图中的一个页面节点,具体的页面就是Activity、DialogFragment还是Fragment。在Navigator中有一个Name注解,它的说明是This annotation should be added to each Navigator subclass to denote the default name used to register the Navigator with a {@link NavigatorProvider}.简单来说就是Navigator子类必须在类上标有Name注解,这个Name是用来注册到NavigatorProvider中的。Navigator中有一个方法@NonNull public abstract D createDestination();,当创建页面节点时,会解析Navigator子类上的Name注解,NavDestination的构造器需要具体的Navigator对象或者这个Navigator类的Name注解,这个Navigator的Name能够取得对应Navigator实例以完成页面跳转;具体的跳转是实现navigate方法;是否拦截系统返回键是实现popBackStack方法;onSaveStateonRestoreState的实现是用来进行状态保存和恢复的操作的;Extras接口是用来处理特定行为的,比如转场动画。

    @CallSuper
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final Context context = requireContext();
        // 这里创建一个NavHostController对象,NavHostController类是NavController的扩展类,
        // 通过NavHost来连接NavController和其它依赖。
        // NavHostController的构造器通过super调用NavController的构造器。
        // NavController类是通过NavHost组织app内导航的类。
        // 在NavController构造器中,有这样两行代码:
        // mNavigatorProvider.addNavigator(new NavGraphNavigator(mNavigatorProvider));
        // mNavigatorProvider.addNavigator(new ActivityNavigator(mContext));
        // mNavigatorProvider是NavigatorProvider类的对象,这个类用于存储
        // 一系列的合法跳转到目的地的Navigators。
        // 在NavigatorProvider类中有这样一个属性:
        // private final HashMap> mNavigators = new HashMap<>();
        // 这个mNavigators是一个HashMap,用来存储所有的Navigators,也就是导航器实例,
        // 其中的key是Navigator子类标记的Name注解,value就是对应Navigator实例。
        // ActivityNavigator是Activity的导航器。
        // NavGraphNavigator是当navigation资源解析完成后跳转到startDestination的导航器。
        mNavController = new NavHostController(context);
        mNavController.setLifecycleOwner(this);
        mNavController.setOnBackPressedDispatcher(requireActivity().getOnBackPressedDispatcher());
        // Set the default state - this will be updated whenever
        // onPrimaryNavigationFragmentChanged() is called
        mNavController.enableOnBackPressed(
                mIsPrimaryBeforeOnCreate != null && mIsPrimaryBeforeOnCreate);
        mIsPrimaryBeforeOnCreate = null;
        mNavController.setViewModelStore(getViewModelStore());
        // onCreateNavController方法调用了下面两个语句
        // navController.getNavigatorProvider()
        // .addNavigator(new DialogFragmentNavigator(requireContext(),getChildFragmentManager()));
        // navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
        // 上面两个语句分别是添加DialogFragment和Fragment的导航器。
        onCreateNavController(mNavController);

        Bundle navState = null;
        if (savedInstanceState != null) {
            navState = savedInstanceState.getBundle(KEY_NAV_CONTROLLER_STATE);
            if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) {
                mDefaultNavHost = true;
                getParentFragmentManager().beginTransaction()
                        .setPrimaryNavigationFragment(this)
                        .commit();
            }
            mGraphId = savedInstanceState.getInt(KEY_GRAPH_ID);
        }

        if (navState != null) {
            // Navigation controller state overrides arguments
            mNavController.restoreState(navState);
        }
        if (mGraphId != 0) {
            // Set from onInflate()
            mNavController.setGraph(mGraphId);
        } else {
            // See if it was set by NavHostFragment.create()
            final Bundle args = getArguments();
            final int graphId = args != null ? args.getInt(KEY_GRAPH_ID) : 0;
            final Bundle startDestinationArgs = args != null
                    ? args.getBundle(KEY_START_DESTINATION_ARGS)
                    : null;
            if (graphId != 0) {
                mNavController.setGraph(graphId, startDestinationArgs);
            }
        }
    }

ActivityNavigator分析

  1. 在ActivityNavigator类上标注了@Navigator.Name("activity")的注解,说明了当ActivityNavigator添加到NavigatorProvider中的mNavigators的HashMap成员变量时,key是"activity",而value是ActivityNavigator本身的实例。
  2. createDestination():创建Activity页面节点
    // 以下是ActivityNavigator类的方法
    // ......
    @NonNull
    @Override
    public Destination createDestination() {
        return new Destination(this);
    }
    // ......
    // 以下是ActivityNavigator的静态内部类Destination的方法
    // ......
    public Destination(@NonNull Navigator activityNavigator) {
        super(activityNavigator);
    }
    // ......
    // 以下是NavDestination类
    // ......
    public NavDestination(@NonNull Navigator navigator) { 
        this(NavigatorProvider.getNameForNavigator(navigator.getClass()));
    }

    public NavDestination(@NonNull String navigatorName) {
        mNavigatorName = navigatorName;
    }
    // ......

首先是在ActivityNavigator中创建它的静态内部类Destination的对象,在这个静态内部类Destination构造器中调用它父类即NavDestination的构造器,然后获取对应的Navigator的Name。这个静态内部类Destination中提供了一些创建Intent对象的方法。

  1. navigate方法:通过第2步中创建的Intent对象来进行跳转。

DialogFragmentNavigator分析

  1. 它的注解变成了@Navigator.Name("dialog"),其余与ActivityNavigator类似。
  2. createDestination():创建DialogFragment页面节点。
    与ActivityNavigator中第2步类似,也是通过静态内部类的方式将Name传递给NavDestination。

在这个静态内部类Destination中有setClassName和getClassName方法来设置和获取与此Destination关联的DialogFragment类名。

  1. navigate方法:获取第2步中的DialogFragment全类名,判断它是否是DialogFragment,最后通过DialogFragment对象的show展示DialogFragment。

FragmentNavigator分析

  1. 它的注解变成了@Navigator.Name("fragment"),其余与DialogFragmentNavigator类似。
  2. createDestination():创建Fragment页面节点,与DialogFragmentNavigator中第2步类似。
  3. navigate方法与DialogFragmentNavigator类似,只不过最后是通过FragmentTransaction对象的replace展示fragment。

NavGraphNavigator分析

  1. 它的注解变成了@Navigator.Name("navigation"),其余与FragmentNavigator类似。
  2. createDestination():创建了一个NavGraph对象
    // 这是NavGraphNavigator类
    // ......
    @NonNull
    @Override
    public NavGraph createDestination() {
        return new NavGraph(this);
    }
    // ......
    // 这是NavGraph类
    // 这个mNodes属性是用来存储一个个的页面节点的
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    final SparseArrayCompat mNodes = new SparseArrayCompat<>();
    // ......
    public NavGraph(@NonNull Navigator navGraphNavigator) {
        super(navGraphNavigator);
    }
    // ......

首先是在NavGraphNavigator中创建NavGraph对象,在NavGraph类的构造器中调用它父类即NavDestination的构造器,然后获取对应的Navigator的Name。NavGraph是按ID获取的{@link NavDestination}节点的集合,它是一个虚拟的destination,本身并不会加入回退栈而是将start destination加入到回退栈实际上配置navigation资源文件的作用就是为了生成NavGraph对象,最后将其和NavController关联

  1. navigate方法实际上是先获取默认页面的start destination,然后获取navigator实例,具体的navigate就是根据navigator是fragment、activity还是dialogfragment执行特定的navigate。

Navigation分析总结

  • NavHostFragment:作为内容区域的承载者
  • NavHostController:NavController的扩展
  • NavController:组织App内的导航。
  • Navigator:定义导航机制,内置子类包括NavGraphNavigator、ActivityNavigator、FragmentNavigator、DialogFragmentNavigator。
  • NavGraph:所有{@link NavDestination}节点的集合,它是一个虚拟的destination,将start destination加入到了回退栈。
  • NavDestination:代表了在整个NavGraph中的一个页面节点,子类包括ActivityNavigator.Destination、FragmentNavigator.Destination、DialogFragmentNavigator.Destination、NavGraph。
  • NavigatorProvider:使用mNavigators的HashMap成员变量存储所有Navigator。

你可能感兴趣的:(Navigation)