Jetpack之Navigation回退之后页面销毁

安卓 专栏收录该内容
61 篇文章 0 订阅

一、出现的现象
使用Navigation进行Fragment之间的跳转,在回退的时候发现之前的Fragment页面上的数据不在了,经排查发现是页面被重新执行了onCreateView,也就是说页面重新被创建了,这样的话跟之前的多Activity的体验效果就不一样了。

二、分析原因
其实这是google故意这么设计的,google的本意是在宿主Activity里有ViewModel,ViewModel维护着页面里的数据,Fragment每次被创建就通过ViewModel获取之前的数据。如果遇到页面是ListView这种,如何在重新创建的时候保持之前的状态就比较麻烦了。那么有什么方法可以解决呢?

三、解决方法
首先我们要知道为何返回的时候Fragment执行了onCreateView,经过分析关键在于FragmentNavigator这个类中的navigate方法

 @Nullable
    @Override
    public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
       	//忽略部分代码
        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);
        }
		//关键在于这个replace,导致每次打开新页面都会把当前的页面给替换了
        ft.replace(mContainerId, frag);
        ft.setPrimaryNavigationFragment(frag);

        final @IdRes int destId = destination.getId();
        final boolean initialNavigation = mBackStack.isEmpty();
        // TODO Build first class singleTop behavior for fragments
        final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
                && navOptions.shouldLaunchSingleTop()
                && mBackStack.peekLast() == destId;
		//忽略部分代码

       
    }

经过源码的查看其实就是打开的新页面把当前的页面给替换了,我们知道可以通过先hide当前页面然后在add新页面不就行了,但是源码我们无法去修改,因为我们可以复制一份自己改,在引用的时候用自己创建的这个类不就行了。
我们拷贝FragmentNavigator类重命名一个MyFragmentNavigator,修改navigate方法

@Nullable
    @Override
    public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
       	//忽略部分代码
        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);
        }
        //先把当前的fragment隐藏,然后add新页面
		if(mFragmentManager.getFragments().size()>0){
            ft.hide(mFragmentManager.getFragments().get(mFragmentManager.getFragments().size()-1));
            ft.add(mContainerId, frag);
        }else {
            ft.replace(mContainerId, frag);
        }
        ft.setPrimaryNavigationFragment(frag);

        final @IdRes int destId = destination.getId();
        final boolean initialNavigation = mBackStack.isEmpty();
        // TODO Build first class singleTop behavior for fragments
        final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
                && navOptions.shouldLaunchSingleTop()
                && mBackStack.peekLast() == destId;
		//忽略部分代码
    }

目前我们创建了MyFragmentNavigator,但是这个MyFragmentNavigator没法直接使用,通过源码分析得知NavHostFragment引用了FragmentNavigator,那么我们肯定也要修改一下NavHostFragment,我们只需要复制一份NavHostFragment重命名为MyNavHostFragment,把里面引用的FragmentNavigato改为我们自己创建的MyFragmentNavigator就可以了。

四、如何使用
我们知道在Activity布局文件里是这样写的

<fragment
        app:defaultNavHost="true"
        app:navGraph="@navigation/my_nav"
        android:id="@+id/nav_host_fragment"
        //把这里改成MyNavHostFragment的全路径就可以了
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </fragment>

然后我们就可以和原来的正常使用就行了,页面的跳转传参都用之前的代码

总结:
1.复制FragmentNavigator为MyFragmentNavigator,修改里面的navigate方法
2.复制NavHostFragment为MyNavHostFragment,将内部引用的FragmentNavigator改为MyFragmentNavigator
3.在主Activity布局文件里替换为MyNavHostFragment的全路径

  • 2
    点赞
  • 6
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值