Navigation返回重新执行onCreateView解决办法


Navigation作为Android jetpack新推出的组件使用起来非常的方便,甚至可以做到一个Activity配合Navigation完成整个应用的开发,但是有的小伙伴可能会发现在使用Navigation时点击返回键后页面重构,重新执行了onCreateView方法,那遇到这种问题怎么办呢

首先我们确定Navigation切换Fragment的方式为replace,这里放出NavController.navigate(R.id.action…)具体的源码

    @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;
            }
            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);
            }
    
            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;
    
            boolean isAdded;
            if (initialNavigation) {
                isAdded = true;
            } else if (isSingleTopReplacement) {
                // Single Top means we only want one instance on the back stack
                if (mBackStack.size() > 1) {
                    // If the Fragment to be replaced is on the FragmentManager's
                    // back stack, a simple replace() isn't enough so we
                    // remove it from the back stack and put our replacement
                    // on the back stack in its place
                    mFragmentManager.popBackStack(
                            generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
                            FragmentManager.POP_BACK_STACK_INCLUSIVE);
                    ft.addToBackStack(generateBackStackName(mBackStack.size(), destId));
                    mIsPendingBackStackOperation = true;
                }
                isAdded = false;
            } else {
                ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
                mIsPendingBackStackOperation = true;
                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();
            // The commit succeeded, update our view of the world
            if (isAdded) {
                mBackStack.add(destId);
                return destination;
            } else {
                return null;
            }
        }

大家知道FragmentManager是具有addToBackStack()回退栈操作的,引用官方的话:
当您移除或替换一个片段并向返回栈添加事务时,系统会停止(而非销毁)移除的片段。 如果用户执行回退操作进行片段恢复,该片段将重新启动。 如果您不向返回栈添加事务,则系统会在您移除或替换片段时将其销毁。
但是我不太清楚这里为什么没有产生作用,即使断点跑进去了还是会销毁View,= =
也许有更加资深的程序员可以给我解答一下吧,我们还是继续说问题的解决方案,在这里我想到了两种,大家细细往后看

第一个方案(利用生命周期保障程序运行):

我发现虽然onCreateView方法重新执行了,但是onAttach等其他生命周期的方法没有重跑,我们可以将代码从onCreateView移动到onAttach或其他不会执行多次的生命周期中,在onCreateView直接返回已经初始化过的View

class LoginFragment : Fragment() {

    @Inject
    lateinit var viewModel: AccountViewModel
    private var databinding: LoginFragmentBinding? = null

    override fun onAttach(context: Context?) {
        EdgeLog.show(javaClass,"生命周期","onAttach")
        super.onAttach(context)
        DaggerAccountComponent.builder().appComponent(App.appComponent).build().inject(this)
        viewModel.fragment.value = this
        databinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.login_fragment, null, false)
        databinding!!.viewModel = viewModel
        databinding!!.lifecycleOwner = this
        databinding!!.button2.setOnClickListener {
            Navigation.findNavController(it).navigate(R.id.action_loginFragment_to_registerFragment)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return databinding!!.root
    }
}

第二个方案(未销毁数据重用)

虽然Fragment的某些生命周期重新执行了,但是数据并未销毁,我们可以将数据重新利用起来

class LoginFragment : Fragment() {

    @Inject
    lateinit var viewModel: AccountViewModel
    private var databinding: LoginFragmentBinding? = null

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        if (viewModel == null || databinding == null) {
            DaggerAccountComponent.builder().appComponent(App.appComponent).build().inject(this)
            viewModel.fragment.value = this
            databinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.login_fragment, null, false)
            databinding!!.viewModel = viewModel
            databinding!!.lifecycleOwner = this
            databinding!!.button2.setOnClickListener {
                Navigation.findNavController(it).navigate(R.id.action_loginFragment_to_registerFragment)
            }
        }
        return databinding!!.root
    }
}

这两个办法肯定不是最好的,希望有其他童鞋可以提出意见或者找到更好的办法!!!

本文标签:
Fragment重启
Navigation销毁
回退栈
返回数据清空

猜你喜欢

转载自blog.csdn.net/WitheredSkull/article/details/88532687
今日推荐