首先我们确定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销毁
回退栈
返回数据清空