Android 导航组件:如何保存片段状态
Android navigation component: how save fragment state
我使用 bottomNavigationView 和导航组件。请告诉我如何在切换到另一个选项卡和 return 到旧选项卡后不破坏片段?例如,我有三个选项卡 - A、B、C。我的起始选项卡是 A。导航到 B 后,然后 return A。当我 return 到选项卡 A 时,我不希望它被重新创建。怎么做?谢谢
根据 open issue,Navigation 不直接支持多个返回堆栈 - 即,当您从 A 或 C 返回 B 时保存堆栈 B 的状态,因为 Fragments 不支持多个返回堆栈。
根据this comment:
The NavigationAdvancedSample is now available at https://github.com/googlesamples/android-architecture-components/tree/master/NavigationAdvancedSample
This sample uses multiple NavHostFragments, one for each bottom navigation tab, to work around the current limitations of the Fragment API in supporting multiple back stacks.
We'll be proceeding with the Fragment API to support multiple back stacks and the Navigation API to plug into it once created, which will remove the need for anything like the NavigationExtensions.kt
file. We'll continue to use this issue to track that work.
因此,您现在可以在您的应用程序中使用 NavigationAdvancedSample 方法并为问题加注星标,以便在基础问题得到解决并向 Navigation 添加直接支持时获得更新。
在 activity 上声明片段并在 onCreate 方法上创建片段实例,然后在 updateFragment 方法中传递片段实例。根据需要创建与底部导航侦听器项目 ID 相对应的尽可能多的片段实例。
Fragment fragmentA;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
fragmentA = new Fragment();
updateFragment(fragmentA);
}
public void updateFragment(Fragment fragment) {
FragmentTransaction transaction =
getSupportFragmentManager().beginTransaction();
transaction.add(R.id.layoutFragment, fragment);
transaction.commit();
}
此外请确保您正在使用 android.support.v4.app.Fragment 并调用 getSupportFragmentManager()
如果您可以处理销毁片段,但又想保存 ViewModel,则可以将其限定在导航图中:
private val viewModel: FavouritesViewModel by
navGraphViewModels(R.id.mobile_navigation) {
viewModelFactory
}
阅读更多here
编辑
正如@SpiralDev 指出的那样,使用 Hilt 会稍微简化一下:
private val viewModel: MainViewModel by
navGraphViewModels(R.id.mobile_navigation) {
defaultViewModelProviderFactory
}
这可以使用 Fragment show/hide 逻辑来实现。
private val bottomFragmentMap = hashMapOf<Int, Fragment>()
bottomFragmentMap[0] = FragmentA.newInstance()
bottomFragmentMap[1] = FragmentB.newInstance()
bottomFragmentMap[2] = FragmentC.newInstance()
bottomFragmentMap[3] = FragmentD.newInstance()
private fun loadFragment(fragmentIndex: Int) {
val fragmentTransaction = childFragmentManager.beginTransaction()
val bottomFragment = bottomFragmentMap[fragmentIndex]!!
// first time case. Add to container
if (!bottomFragment.isAdded) {
fragmentTransaction.add(R.id.container, bottomFragment)
}
// hide remaining fragments
for ((key, value) in bottomFragmentMap) {
if (key == fragmentIndex) {
fragmentTransaction.show(value)
} else if (value.isVisible) {
fragmentTransaction.hide(value)
}
}
fragmentTransaction.commit()
}
更新:
使用最新版本的片段导航组件,自行处理片段状态。参见 this sample
旧:
class BaseViewModel : ViewModel() {
val bundleFromFragment = MutableLiveData<Bundle>()
}
class HomeViewModel : BaseViewModel () {
... HomeViewModel logic
}
主页片段内部(底部导航选项卡)
private var viewModel: HomeViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.bundleFromFragment.observe(viewLifecycleOwner, Observer {
val message = it.getString("ARGUMENT_MESSAGE", "")
binding.edtName.text = message
})
}
override fun onDestroyView() {
super.onDestroyView()
viewModel.bundleFromFragment.value = bundleOf(
"ARGUMENT_MESSAGE" to binding.edtName.text.toString(),
"SCROLL_POSITION" to binding.scrollable.scrollY
)
}
您可以对底部导航中的所有片段执行此模式
只需使用2.4.0-alpha01或以上版本的导航组件
2021 年更新
使用 2.4.0-alpha05 或以上版本。
不要使用这个 或其他
我使用 bottomNavigationView 和导航组件。请告诉我如何在切换到另一个选项卡和 return 到旧选项卡后不破坏片段?例如,我有三个选项卡 - A、B、C。我的起始选项卡是 A。导航到 B 后,然后 return A。当我 return 到选项卡 A 时,我不希望它被重新创建。怎么做?谢谢
根据 open issue,Navigation 不直接支持多个返回堆栈 - 即,当您从 A 或 C 返回 B 时保存堆栈 B 的状态,因为 Fragments 不支持多个返回堆栈。
根据this comment:
The NavigationAdvancedSample is now available at https://github.com/googlesamples/android-architecture-components/tree/master/NavigationAdvancedSample
This sample uses multiple NavHostFragments, one for each bottom navigation tab, to work around the current limitations of the Fragment API in supporting multiple back stacks.
We'll be proceeding with the Fragment API to support multiple back stacks and the Navigation API to plug into it once created, which will remove the need for anything like the
NavigationExtensions.kt
file. We'll continue to use this issue to track that work.
因此,您现在可以在您的应用程序中使用 NavigationAdvancedSample 方法并为问题加注星标,以便在基础问题得到解决并向 Navigation 添加直接支持时获得更新。
在 activity 上声明片段并在 onCreate 方法上创建片段实例,然后在 updateFragment 方法中传递片段实例。根据需要创建与底部导航侦听器项目 ID 相对应的尽可能多的片段实例。
Fragment fragmentA;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
fragmentA = new Fragment();
updateFragment(fragmentA);
}
public void updateFragment(Fragment fragment) {
FragmentTransaction transaction =
getSupportFragmentManager().beginTransaction();
transaction.add(R.id.layoutFragment, fragment);
transaction.commit();
}
此外请确保您正在使用 android.support.v4.app.Fragment 并调用 getSupportFragmentManager()
如果您可以处理销毁片段,但又想保存 ViewModel,则可以将其限定在导航图中:
private val viewModel: FavouritesViewModel by
navGraphViewModels(R.id.mobile_navigation) {
viewModelFactory
}
阅读更多here
编辑
正如@SpiralDev 指出的那样,使用 Hilt 会稍微简化一下:
private val viewModel: MainViewModel by
navGraphViewModels(R.id.mobile_navigation) {
defaultViewModelProviderFactory
}
这可以使用 Fragment show/hide 逻辑来实现。
private val bottomFragmentMap = hashMapOf<Int, Fragment>()
bottomFragmentMap[0] = FragmentA.newInstance()
bottomFragmentMap[1] = FragmentB.newInstance()
bottomFragmentMap[2] = FragmentC.newInstance()
bottomFragmentMap[3] = FragmentD.newInstance()
private fun loadFragment(fragmentIndex: Int) {
val fragmentTransaction = childFragmentManager.beginTransaction()
val bottomFragment = bottomFragmentMap[fragmentIndex]!!
// first time case. Add to container
if (!bottomFragment.isAdded) {
fragmentTransaction.add(R.id.container, bottomFragment)
}
// hide remaining fragments
for ((key, value) in bottomFragmentMap) {
if (key == fragmentIndex) {
fragmentTransaction.show(value)
} else if (value.isVisible) {
fragmentTransaction.hide(value)
}
}
fragmentTransaction.commit()
}
更新: 使用最新版本的片段导航组件,自行处理片段状态。参见 this sample
旧:
class BaseViewModel : ViewModel() {
val bundleFromFragment = MutableLiveData<Bundle>()
}
class HomeViewModel : BaseViewModel () {
... HomeViewModel logic
}
主页片段内部(底部导航选项卡)
private var viewModel: HomeViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.bundleFromFragment.observe(viewLifecycleOwner, Observer {
val message = it.getString("ARGUMENT_MESSAGE", "")
binding.edtName.text = message
})
}
override fun onDestroyView() {
super.onDestroyView()
viewModel.bundleFromFragment.value = bundleOf(
"ARGUMENT_MESSAGE" to binding.edtName.text.toString(),
"SCROLL_POSITION" to binding.scrollable.scrollY
)
}
您可以对底部导航中的所有片段执行此模式
只需使用2.4.0-alpha01或以上版本的导航组件
2021 年更新
使用 2.4.0-alpha05 或以上版本。
不要使用这个