欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android-Jetpack笔记-Navigation之Fragment使用

程序员文章站 2022-03-13 17:08:53
...

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,运行如下:

Android-Jetpack笔记-Navigation之Fragment使用

先看下布局文件,

<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视图,

Android-Jetpack笔记-Navigation之Fragment使用

这样可以用可视化的方式管理导航图结构,然后来看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,会发现有多个实现类,

Android-Jetpack笔记-Navigation之Fragment使用

这里我们使用的是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-Jetpack笔记-Navigation之Fragment使用

相关标签: Android