Jetpack之Navigation

1,273 阅读7分钟

1.基本使用

引入库

def nav_version = "2.3.2"
// Java language implementation
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"

Activity布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <!--
    app:defaultNavHost="true"
    拦截系统back键
    -->
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/my_nav_host_fragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="9"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph_main"/>
       
</LinearLayout>

在res/navigation目录下nav_graph_main.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_main.xml"
    app:startDestination="@id/page1Fragment">
    <fragment
        android:id="@+id/page1Fragment"
        android:name="com.example.lsn4_navigationdemo.MainPage1Fragment"
        android:label="fragment_page1"
        tools:layout="@layout/fragment_main_page1">
            
        <!--action:程序中使用id跳到destination对应的类-->
        <action
            android:id="@+id/action_page2"
            app:destination="@id/page2Fragment" />
    </fragment>
                
    <fragment
        android:id="@+id/page2Fragment"
        android:name="com.example.lsn4_navigationdemo.MainPage2Fragment"
        android:label="fragment_page2"
        tools:layout="@layout/fragment_main_page2">
        <action
            android:id="@+id/action_page1"
            app:destination="@id/page1Fragment" />
        <action
            android:id="@+id/action_page3"
            app:destination="@id/page3Fragment" />
    </fragment>

    <!--    <navigation-->
    <!--        android:id="@+id/nav_graph_page3"-->
    <!--        app:startDestination="@id/page3Fragment">-->
    <fragment
        android:id="@+id/page3Fragment"
        android:name="com.example.lsn4_navigationdemo.MainPage3Fragment"
        android:label="fragment_page3"
        tools:layout="@layout/fragment_main_page3"
        >
        <action
            android:id="@+id/action_page2"
            app:destination="@id/page2Fragment"/>
    </fragment>


</navigation>

Fragment中调用跳转

//方式一:直接跳入指定的fragment
Navigation.findNavController(view).navigate(R.id.page2Fragment);  

//方式二:通过action
Navigation.findNavController(view).navigate(R.id.action_page2);   //跳入page2

Activity中调用跳转

//获取controller方式一
NavController controller=Navigation.findNavController(this,R.id.my_nav_host_fragment);

//获取controller方式二
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.my_nav_host_fragment);
NavController controller = navHostFragment.getNavController();

//跳转
controller.navigate(R.id.page2Fragment);

Activity绑定navigation

除了在Activity的布局中指定navigation布局资源以外,还可以通过java代码进行设置

public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //方式一
        val finalHost = NavHostFragment.create(R.navigation.nav_graph_main)
        supportFragmentManager.beginTransaction()
                .replace(R.id.ll_fragment_navigation, finalHost)
                .setPrimaryNavigationFragment(finalHost)
                .commit();
    }

    //方式二
    @Override
    public boolean onSupportNavigateUp() {
        Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.my_nav_host_fragment);
        return NavHostFragment.findNavController(fragment).navigateUp();
    }
}

底部导航

引入底部导航控件

implementation 'com.google.android.material:material:1.1.0'

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <!--
    app:defaultNavHost="true"
    拦截系统back键
    -->
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/my_nav_host_fragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="9"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph_main"/>
        
    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/nav_view"
        android:layout_width="match_parent"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        app:itemTextColor="#ff0000"
        app:menu="@menu/menu"/>

</LinearLayout>

menu

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/page1Fragment"
        android:icon="@drawable/ic_launcher_foreground"
        android:title="1"/>
    <item
        android:id="@+id/page2Fragment"
        android:icon="@drawable/ic_launcher_foreground"
        android:title="2"/>
    <item
        android:id="@+id/page3Fragment"
        android:icon="@drawable/ic_launcher_foreground"
        android:title="3"/>

</menu>

Activity绑定导航

public class MainActivity extends AppCompatActivity {

    BottomNavigationView bottomNavigationView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.action_page1);
        bottomNavigationView=findViewById(R.id.nav_view);
        
        //获取controller方式一
        //NavController controller=Navigation.findNavController(this,R.id.my_nav_host_fragment);
       
        //获取controller方式二
        NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.my_nav_host_fragment);
        NavController controller = navHostFragment.getNavController();
        
        //绑定导航
        NavigationUI.setupWithNavController(bottomNavigationView,controller);

}

2.原理

初始化过程 NavHostFragment生命周期方法

1.create—NavHostFragment的创建

在NavHostFragment.create方法

  1. 初始化Bundle,并且将graphResId,startDestinationArgs存储在Bundle中。
  2. new NavHostFragment()返回NavHostFragment实例。
    //NavHostFragment.java
    @NonNull
    public static NavHostFragment create(@NavigationRes int graphResId,
            @Nullable Bundle startDestinationArgs) {
        Bundle b = null;
        if (graphResId != 0) {
            b = new Bundle();
            b.putInt(KEY_GRAPH_ID, graphResId);
        }
        if (startDestinationArgs != null) {
            if (b == null) {
                b = new Bundle();
            }
            b.putBundle(KEY_START_DESTINATION_ARGS, startDestinationArgs);
        }

        final NavHostFragment result = new NavHostFragment();
        if (b != null) {
            result.setArguments(b);
        }
        return result;
    }

2.onInflate—XML文件的解析

主要是解析布局文件的两个属性:defaultNavHost和navGraph,并且初始化全局变量。

NavHostFragment.onInflate方法 当Fragment以XML的方式静态加载时,最先会调用onInflate的方法(调用时机:Fragment所关联的Activity在执行setContentView时)。

    //NavHostFragment.java
    @CallSuper
    @Override
    public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs,
            @Nullable Bundle savedInstanceState) {
        super.onInflate(context, attrs, savedInstanceState);

        final TypedArray navHost = context.obtainStyledAttributes(attrs,
                androidx.navigation.R.styleable.NavHost);
        final int graphId = navHost.getResourceId(
                androidx.navigation.R.styleable.NavHost_navGraph, 0);
        if (graphId != 0) {
            mGraphId = graphId;   //navigation的图布局
        }
        navHost.recycle();

        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NavHostFragment);
        final boolean defaultHost = a.getBoolean(R.styleable.NavHostFragment_defaultNavHost, false);
        if (defaultHost) {
            mDefaultNavHost = true;    //是否监听物理返回键
        }
        a.recycle();
    }

3.onCreateNavController—创建Navigator

在实现导航的时候,我们需要根据navigation配置文件生成NavGraph类,然后在根据每个不同的actionid,找到对应的NavDestination就可以实现页面导航跳转了。

创建Navigator

Navigator类的作用是:能够实例化对应的NavDestination,并且能够实现导航功能,拥有自己的回退栈。

    @CallSuper
    protected void onCreateNavController(@NonNull NavController navController) {
        navController.getNavigatorProvider().addNavigator(
                new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
        
        //创建Navigator并绑定到NavigatorProvider中。
        //mNavigatorProvider是NavController中的全局变量,内部通过HashMap键值对的形式保存Navigator类。
        navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
    }
  1. 其中mNavigatorProvider是NavController中的全局变量,内部通过HashMap键值对的形式保存Navigator类
  2. createFragmentNavigator方法,构建了FragmentNavigator对象,其中抽象类Navigator还有个重要的实现类ActivityNavigator和NavGraphNavigator。这个两个类的对象在NavController的构造方法中被添加。
//NavController.java
public NavController(@NonNull Context context) {
        mContext = context;
        while (context instanceof ContextWrapper) {
            if (context instanceof Activity) {
                mActivity = (Activity) context;
                break;
            }
            context = ((ContextWrapper) context).getBaseContext();
        }
        mNavigatorProvider.addNavigator(new NavGraphNavigator(mNavigatorProvider));
        mNavigatorProvider.addNavigator(new ActivityNavigator(mContext));
}

4.onCreate—导航初始化

无论是XML实现还是代码实现,都会执行Fragment的onCreate方法。NavController在这里被创建,并且NavHostFragment中有一个NavController对象。

   @CallSuper
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        final Context context = requireContext();
        
        //1.初始化NavController,NavController为导航的控制类,核心类
        mNavController = new NavHostController(context);
        mNavController.setLifecycleOwner(this);
        mNavController.setOnBackPressedDispatcher(requireActivity().getOnBackPressedDispatcher());
     
        mNavController.enableOnBackPressed(
                mIsPrimaryBeforeOnCreate != null && mIsPrimaryBeforeOnCreate);
        mIsPrimaryBeforeOnCreate = null;
        mNavController.setViewModelStore(getViewModelStore());
        onCreateNavController(mNavController);

        Bundle navState = null;
        //2.开始恢复状态
        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) {
            mNavController.restoreState(navState);
        }
        
        //3.设置导航图信息
        if (mGraphId != 0) {
            mNavController.setGraph(mGraphId);
        } else {
            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);
            }
        }
        super.onCreate(savedInstanceState);
    }

5.onCreateView

NavHostFragment的视图就只有一个FragmentContainerView 继承 FrameLayout

    //NavHostFragment.java
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        FragmentContainerView containerView = new FragmentContainerView(inflater.getContext());
        
        //这行主要用于以代码方式添加fragment
        containerView.setId(getContainerId());
        return containerView;
    }

6.onViewCreated

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if (!(view instanceof ViewGroup)) {
            throw new IllegalStateException("created host view " + view + " is not a ViewGroup");
        }
        //把mNavController记录在view的tag中
        Navigation.setViewNavController(view, mNavController);

        if (view.getParent() != null) {
            mViewParent = (View) view.getParent();
            if (mViewParent.getId() == getId()) {
                //把mNavController记录在view的tag中
                Navigation.setViewNavController(mViewParent, mNavController);
            }
        }
    }

获取NavController

1.获取NavController

NavHostFragment.findNavController(fragment)
    //NavHostFragment.java
    @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.getParentFragmentManager()
                    .getPrimaryNavigationFragment();
            if (primaryNavFragment instanceof NavHostFragment) {
                return ((NavHostFragment) primaryNavFragment).getNavController();
            }
            findFragment = findFragment.getParentFragment();
        }

        View view = fragment.getView();
        if (view != null) {
            return Navigation.findNavController(view);
        }
        
        Dialog dialog = fragment instanceof DialogFragment
                ? ((DialogFragment) fragment).getDialog()
                : null;
        if (dialog != null && dialog.getWindow() != null) {
            return Navigation.findNavController(dialog.getWindow().getDecorView());
        }

        throw new IllegalStateException("Fragment " + fragment
                + " does not have a NavController set");
    }

2.Navigation中findNavController

3.findViewNavController

通过view.tag查找NavController。内部调用了getViewNavController方法。

4.getViewNavController

getViewNavController方法 通过获取view的Tag,获取NavController对象,这里的tag ID和setViewNavController都是nav_controller_view_tag。

    //Navigation.java
    @NonNull
    public static NavController findNavController(@NonNull View view) {
        //3.
        NavController navController = findViewNavController(view);
        if (navController == null) {
            throw new IllegalStateException("View " + view + " does not have a NavController set");
        }
        return navController;
    }


     @Nullable
    private static NavController findViewNavController(@NonNull View view) {
        while (view != null) {
            NavController controller = getViewNavController(view);
            if (controller != null) {
                return controller;
            }
            ViewParent parent = view.getParent();
            view = parent instanceof View ? (View) parent : null;
        }
        return null;
    }

    @Nullable
    private static NavController getViewNavController(@NonNull View view) {
        //4.这里的tag ID和setViewNavController都是nav_controller_view_tag。
        Object tag = view.getTag(R.id.nav_controller_view_tag);
        NavController controller = null;
        if (tag instanceof WeakReference) {
            controller = ((WeakReference<NavController>) tag).get();
        } else if (tag instanceof NavController) {
            controller = (NavController) tag;
        }
        return controller;
    }

导航navigate

navigate

在构建和获取到NavController对象以及NavGraph之后。下面是使用它来实现真正的导航了。下面从navigate开始分析。在navigate方法内部会查询到NavDestination,然后根据不同的Navigator实现页面导航。

public void navigate(@IdRes int resId, @Nullable Bundle args, @Nullable NavOptions navOptions,
            @Nullable Navigator.Extras navigatorExtras) {
        NavDestination currentNode = mBackStack.isEmpty()
                ? mGraph
                : mBackStack.getLast().getDestination();
        if (currentNode == null) {
            throw new IllegalStateException("no current navigation node");
        }
        @IdRes int destId = resId;
        //2.根据id,获取对应的NavAction
        final NavAction navAction = currentNode.getAction(resId);
        Bundle combinedArgs = null;
        if (navAction != null) {
            if (navOptions == null) {
                navOptions = navAction.getNavOptions();
            }
            //3.通过NavAction获取目的地id
            destId = navAction.getDestinationId();
            Bundle navActionArgs = navAction.getDefaultArguments();
            if (navActionArgs != null) {
                combinedArgs = new Bundle();
                combinedArgs.putAll(navActionArgs);
            }
        }

        if (args != null) {
            if (combinedArgs == null) {
                combinedArgs = new Bundle();
            }
            combinedArgs.putAll(args);
        }

        if (destId == 0 && navOptions != null && navOptions.getPopUpTo() != -1) {
            popBackStack(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive());
            return;
        }

        if (destId == 0) {
            throw new IllegalArgumentException("Destination id == 0 can only be used"
                    + " in conjunction with a valid navOptions.popUpTo");
        }
        //4.利用目的地ID属性,通过findDestination方法,找到准备导航的目的地
        NavDestination node = findDestination(destId);
        if (node == null) {
            final String dest = NavDestination.getDisplayName(mContext, destId);
            if (navAction != null) {
                throw new IllegalArgumentException("Navigation destination " + dest
                        + " referenced from action "
                        + NavDestination.getDisplayName(mContext, resId)
                        + " cannot be found from the current destination " + currentNode);
            } else {
                throw new IllegalArgumentException("Navigation action/destination " + dest
                        + " cannot be found from the current destination " + currentNode);
            }
        }
        //5.开始导航
        navigate(node, combinedArgs, navOptions, navigatorExtras);
    }


 private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
        boolean popped = false;
        boolean launchSingleTop = false;
        if (navOptions != null) {
            if (navOptions.getPopUpTo() != -1) {
                popped = popBackStackInternal(navOptions.getPopUpTo(),
                        navOptions.isPopUpToInclusive());
            }
        }
        Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
                node.getNavigatorName());
        Bundle finalArgs = node.addInDefaultArgs(args);
        NavDestination newDest = navigator.navigate(node, finalArgs,
                navOptions, navigatorExtras);
        ...
 }

findDestination

如果回退栈为null返回NavGraph,不为null返回回退栈中的最后一项。

  NavDestination findDestination(@IdRes int destinationId) {
        if (mGraph == null) {
            return null;
        }
        if (mGraph.getId() == destinationId) {
            return mGraph;
        }
        //1.如果回退栈为null返回NavGraph,不为null返回回退栈中的最后一项。
        NavDestination currentNode = mBackStack.isEmpty()
                ? mGraph
                : mBackStack.getLast().getDestination();
        NavGraph currentGraph = currentNode instanceof NavGraph
                ? (NavGraph) currentNode
                : currentNode.getParent();
        return currentGraph.findNode(destinationId);
    }

FragmentNavigator的实现

通过以上的分析,又来到了Navigator 的子类FragmentNavigator类。下面来看看FragmentNavigator.navigate的方法。 (1)调用instantiateFragment,通过反射机制构建Fragment实例
(2)处理进出场等动画逻辑
(3)最终调用FragmentManager来处理导航逻辑。

@Nullable
@Override
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
        @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
    if (mFragmentManager.isStateSaved()) {
        Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
                + " saved its state");
        return null;
    }
    String className = destination.getClassName();
    if (className.charAt(0) == '.') {
        className = mContext.getPackageName() + className;
    }
    //通过反射机制构建Fragment实例 
    final Fragment frag = instantiateFragment(mContext, mFragmentManager,
            className, args);
    frag.setArguments(args);
    final FragmentTransaction ft = mFragmentManager.beginTransaction();
    
    //处理动画逻辑
    int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
    int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
    int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
    int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
    if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
        enterAnim = enterAnim != -1 ? enterAnim : 0;
        exitAnim = exitAnim != -1 ? exitAnim : 0;
        popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
        popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
        ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
    }
    
    //FragmentManager来处理导航逻辑
    ft.replace(mContainerId, frag);
    ft.setPrimaryNavigationFragment(frag);

    final @IdRes int destId = destination.getId();
    final boolean initialNavigation = mBackStack.isEmpty();
    final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
            && navOptions.shouldLaunchSingleTop()
            && mBackStack.peekLast() == destId;

    boolean isAdded;
    if (initialNavigation) {
        isAdded = true;
    } else if (isSingleTopReplacement) {
        if (mBackStack.size() > 1) {
            mFragmentManager.popBackStack(
                    generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
            ft.addToBackStack(generateBackStackName(mBackStack.size(), destId));
        }
        isAdded = false;
    } else {
        ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
        isAdded = true;
    }
    if (navigatorExtras instanceof Extras) {
        Extras extras = (Extras) navigatorExtras;
        for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) {
            ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
        }
    }
    ft.setReorderingAllowed(true);
    ft.commit();
    if (isAdded) {
        mBackStack.add(destId);
        return destination;
    } else {
        return null;
    }
}

ActivityNavigator

ActivityNavigator最终也是调用了startActivity方法,请自己阅读源码。

    @Nullable
    @Override
    public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
           ....
           if (navigatorExtras instanceof Extras) {
                Extras extras = (Extras) navigatorExtras;
                ActivityOptionsCompat activityOptions = extras.getActivityOptions();
                if (activityOptions != null) {
                    ActivityCompat.startActivity(mContext, intent, activityOptions.toBundle());
                } else {
                    mContext.startActivity(intent);
                }
           } else {
               mContext.startActivity(intent);
           }
           ...
    }   

3.总结

  1. NavHostFragment 作为导航载体,在Activity的layout文件里被引用(或者在代码中动态),并且持有导航控制类NavController引用。
  2. NavController 将导航任务委托给Navigator类,Navigator类有两个重要的子类FragmentNavigator和ActivityNavigator子类。NavController类持有NavInflater类引用。
  3. NavInflater 负责解析Navgation文件,负责构建NavGraph导航图。
  4. NavDestination 存有各个目的地信息,在FragmentNavigator和ActivityNavigator内部分别对应一个Destination类,该类继承NavDestination。
  5. 在页面导航时,fragment的操作还是交由FragmentManager在操作,activity交由startActivity执行。

image-20210421174444170