自定义Navigator切换fragment

使用场景:

使用Navigation完成fragment间的跳转操作。


问题描述:

Navigation 实现 fragment 间的跳转用的是replace()方法,此方法会移除原来的fragment,再添加新的fragment,所以回到上一个fragment时就需要重新走一遍生命周期流程,重新加载数据。


解决方案:

分析 NavController类 中的navigate 源码

private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
        ...
        Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
                node.getNavigatorName());  根据节点名称生成不同的navigator
        Bundle finalArgs = node.addInDefaultArgs(args);
        NavDestination newDest = navigator.navigate(node, finalArgs,
                navOptions, navigatorExtras);  调用navigator 中的 navigate方法
       ...
    }

getNavigator 源码如下

private final HashMap<String, Navigator<? extends NavDestination>> mNavigators =
        new HashMap<>();

public <T extends Navigator<?>> T getNavigator(@NonNull String name) {
    if (!validateName(name)) {
        throw new IllegalArgumentException("navigator name cannot be an empty string");
    }

    Navigator<? extends NavDestination> navigator = mNavigators.get(name); // 根据传入的node获取不同的navigator
    if (navigator == null) {
        throw new IllegalStateException("Could not find Navigator with name \"" + name
                + "\". You must call NavController.addNavigator() for each navigation type.");
    }
    return (T) navigator;
}

因此,想要调用自定义navigate()方法就需要 自定义一个Navigator类,同时改变fragment节点名称,将fragment节点名称与Navigator类作为key,value 添加到 HashMap类型的mNavigators中。

自定义Navigator

@Navigator.Name("custom_fragment")  // 节点名称定义为custom_fragment,作为 mNavigatorProvider 变量的 key
class CustomNavigator(  //作为 mNavigatorProvider 变量的 value
    private val context: Context,
    private val manager: FragmentManager,
    private val containerId: Int
) : FragmentNavigator(context, manager, containerId) {

    override fun navigate(  // 重写 navigate 方法
        destination: Destination,
        args: Bundle?,
        navOptions: NavOptions?,
        navigatorExtras: Navigator.Extras?
    ): NavDestination? {
        val tag = destination.id.toString() // 跳转目的地
        val transaction = manager.beginTransaction() // 开启fragment事务

        val currentFragment = manager.primaryNavigationFragment // navigation 顶层fragment
        if (currentFragment != null) {
            transaction.hide(currentFragment)   // 隐藏当前fragment
        }

        var fragment = manager.findFragmentByTag(tag)   // 找到目的地fragment
        if (fragment == null) { // fragment未被初始化
            val className = destination.className
            fragment = manager.fragmentFactory.instantiate(context.classLoader, className) // 实例化 fragment
            transaction.add(containerId, fragment, tag) // 将碎片添加到容器中
        } else { // fragment 已经初始化过了
            transaction.show(fragment) // 显示fragment
        }

        transaction.setPrimaryNavigationFragment(fragment) //将fragment 设置为顶层fragment
        transaction.setReorderingAllowed(true)
        transaction.commitNow() // 提交事务

        return destination // 返回目的地,用于监听
    }
}

修改节点名称

因为节点名称定义为custom_fragment,所以修改为<custom_fragment>

<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/my_nav"
    app:startDestination="@id/home_dest"
    tools:ignore="UnusedNavigation">

    <custom_fragment
        android:id="@+id/home_dest"
        android:name="com.cl.androidstudy.ui.home.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" />
    <custom_fragment
        android:id="@+id/system_dest"
        android:name="com.cl.navicationtest.SystemFragment"
        android:label="fragment_system"
        tools:layout="@layout/fragment_system" />
    <custom_fragment
        android:id="@+id/square_dest"
        android:name="com.cl.navicationtest.SquareFragment"
        android:label="fragment_square"
        tools:layout="@layout/fragment_square" />
    <custom_fragment
        android:id="@+id/me_dest"
        android:name="com.cl.androidstudy.ui.me.MeFragment"
        android:label="fragment_me"
        tools:layout="@layout/fragment_me" />
</navigation>

逻辑代码

val navController = Navigation.findNavController(this, R.id.fragment) // 创建navController
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment)!!
val navigator = CustomNavigator(
    this,
    navHostFragment.childFragmentManager,
    R.id.fragment
)// 生成自定义Navigator对象
navController.navigatorProvider.addNavigator("custom_fragment", navigator) // 添加 key, value
navController.setGraph(R.navigation.my_nav)  // 要在 CustomNavigator 类被加载之后添加graph,不然找不到 custom_fragment节点

参考博客: https://juejin.im/post/6844903896104747022#heading-2

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值