【发布时间】:2019-10-05 08:09:02
【问题描述】:
我使用bottomNavigationView 和导航组件。请告诉我如何在切换到另一个选项卡并返回旧选项卡后不破坏片段?例如,我有三个选项卡 - A、B、C。我的开始选项卡是 A。导航到 B 后,然后返回 A。当我返回选项卡 A 时,我不希望重新创建它。怎么做?谢谢
【问题讨论】:
标签: android navigation android-architecture-navigation
我使用bottomNavigationView 和导航组件。请告诉我如何在切换到另一个选项卡并返回旧选项卡后不破坏片段?例如,我有三个选项卡 - A、B、C。我的开始选项卡是 A。导航到 B 后,然后返回 A。当我返回选项卡 A 时,我不希望重新创建它。怎么做?谢谢
【问题讨论】:
标签: android navigation android-architecture-navigation
根据open issue,Navigation 不直接支持多个回栈 - 即,当您从 A 或 C 返回 B 时保存堆栈 B 的状态,因为 Fragments 不支持多个回栈。
根据this comment:
NavigationAdvancedSample 现已在https://github.com/googlesamples/android-architecture-components/tree/master/NavigationAdvancedSample 提供
此示例使用多个 NavHostFragment,每个底部导航选项卡一个,以解决 Fragment API 在支持多个返回堆栈方面的当前限制。
我们将继续使用 Fragment API 以支持多个返回堆栈,并在创建后将 Navigation API 插入其中,这将不再需要像
NavigationExtensions.kt文件这样的东西。我们将继续使用此问题来跟踪这项工作。
因此,您现在可以在您的应用中使用 NavigationAdvancedSample 方法并为问题加注星标,以便在解决基本问题并将直接支持添加到 Navigation 时获得更新。
【讨论】:
<include> tag 引用单独的图表,而不是复制/粘贴。
如果您可以处理销毁片段,但又想保存 ViewModel,则可以将其范围限定在 Navigation Graph 中:
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
}
【讨论】:
private val viewModel: MainViewModel by navGraphViewModels(R.id.my_nav) { defaultViewModelProviderFactory }
只使用2.4.0-alpha01或以上版本的导航组件
【讨论】:
更新: 使用片段导航组件的最新版本,自行处理片段状态。见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
)
}
您可以对底部导航中的所有片段执行此模式
【讨论】:
2021 年更新 使用 2.4.0-alpha05 或更高版本。 请勿使用此answer 或其他等。
【讨论】:
这可以使用片段显示/隐藏逻辑来实现。
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()
}
【讨论】:
在活动上声明片段并在 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()
【讨论】: