Android-Jetpack笔记-Navigation之Fragment使用

时间:2022-07-23
本文章向大家介绍Android-Jetpack笔记-Navigation之Fragment使用,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Navigation是一种导航的概念,即把Activityfragment当成一个个的目的地Destination,各目的地形成一张导航图NavGraph,由导航控制器NavController来统一调度跳转,本文会先简单分析下AS自带的示例代码。

Jetpack笔记代码

本文源码基于SDK 29,IDE是Android studio 3.5.3

使用

创建工程,引入依赖,

implementation 'androidx.navigation:navigation-fragment:2.2.2'
implementation 'androidx.navigation:navigation-ui:2.2.2'

然后new activity,选中bottom navigation activity,IDE会创建出3个fragment和viewModel,1个activity和布局文件,1个菜单文件bottom_nav_menu,1个导航图文件mobile_navigation,运行如下:

先看下布局文件,

<androidx.constraintlayout.widget.ConstraintLayout>
    <!-- 底部的导航view,菜单文件里定义了3个item -->
    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/nav_view"
        app:menu="@menu/bottom_nav_menu" />

    <!-- fragment作为页面容器,navGraph指定了导航图的结构 -->
    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost="true"
        app:navGraph="@navigation/mobile_navigation" />

</androidx.constraintlayout.widget.ConstraintLayout>

来到导航图文件mobile_navigation

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/mobile_navigation"
    app:startDestination="@+id/navigation_home">

    <fragment
        android:id="@+id/navigation_home"
        android:name="com.holiday.jetpackstudy.navigation.ui.home.HomeFragment"
        tools:layout="@layout/fragment_home" />

    <fragment
        android:id="@+id/navigation_dashboard"
        android:name="com.holiday.jetpackstudy.navigation.ui.dashboard.DashboardFragment"
        tools:layout="@layout/fragment_dashboard" />

    <fragment
        android:id="@+id/navigation_notifications"
        android:name="com.holiday.jetpackstudy.navigation.ui.notifications.NotificationsFragment"
        tools:layout="@layout/fragment_notifications" />
</navigation>

这里列出了所有目的地,其中startDestination指定了导航图的起点即首页HomeFragment,把AS切换成design视图,

这样可以用可视化的方式管理导航图结构,然后来看activity,

public class NavigationActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_navigation);
        BottomNavigationView navView = findViewById(R.id.nav_view);
        //用3个目的地fragment构建配置类
        AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
                R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications)
                .build();
        //用fragment容器构建导航控制器
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        //为导航控制器设置配置类
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
        //关联NavigationView和导航控制器
        NavigationUI.setupWithNavController(navView, navController);
    }

}

代码大致就这些,接下来看看内部实现。

原理

AppBarConfiguration.Builder将目的地(以下目的地均指页面)存储起来,

//AppBarConfiguration.java
 Builder(int... topLevelDestinationIds) {
     for (int destinationId : topLevelDestinationIds) {
         mTopLevelDestinations.add(destinationId);
     }
 }

NavigationUI.setupActionBarWithNavController也是简单的设置参数,

//NavigationUI.java
void setupActionBarWithNavController(AppCompatActivity activity,NavController navController,
                                     AppBarConfiguration configuration) {
    navController.addOnDestinationChangedListener(
        new ActionBarOnDestinationChangedListener(activity, configuration));
}

然后NavigationUI.setupWithNavController关联了NavigationView和导航控制器,

//NavigationUI.java
void setupWithNavController(final BottomNavigationView bottomNavigationView,
            final NavController navController) {
    //设置底部导航的点击事件
    bottomNavigationView.setOnNavigationItemSelectedListener(
        new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                //底部导航切换按钮时
                return onNavDestinationSelected(item, navController);
            }
        });
    //在目的地发生切换的时候,更新底部导航的选中状态,先不看
    navController.addOnDestinationChangedListener(xxx)
}

boolean onNavDestinationSelected(MenuItem item,NavController navController) {
    //导航
    navController.navigate(item.getItemId(), null, options);
}

来到NavController

//NavController.java
//省略调用链来到
void navigate(NavDestination node, Bundle args,
              NavOptions navOptions, Navigator.Extras navigatorExtras) {
    NavDestination newDest = navigator.navigate(node, finalArgs,
                navOptions, navigatorExtras);
}

点进navigator.navigate,会发现有多个实现类,

这里我们使用的是FragmentNavigator

//FragmentNavigator.java
NavDestination navigate(Destination destination, Bundle args,
            NavOptions navOptions, Navigator.Extras navigatorExtras) {
    //获取fragment类名
    String className = destination.getClassName();
    //反射创建fragment
    Fragment frag = instantiateFragment(mContext, mFragmentManager,
                className, args);
    //熟悉的fragment事务
    FragmentTransaction ft = mFragmentManager.beginTransaction();
    //用replace的方式展示fragment
    ft.replace(mContainerId, frag);
    //提交事务
    ft.commit();
}

这里可以看出一个问题,每次切换目的地,fragment是反复销毁重建的,按照谷歌推荐的1个APP只需1个activity的思路开发,这样是没问题的,但是这里的fragment是作为首页的3个常驻页面,我们是希望能够保存起来的,毕竟,销毁重建需要重新请求网络数据,重新初始化view,严重影响用户体验,笔者将在下篇文章探讨fragment的复用方案。

参考文章

  • 掘金-Android官方架构组件Navigation:大巧不工的Fragment管理框架