Android导航组件:如何保存fragment状态

问题描述 投票:0回答:8

我使用bottomNavigationView和导航组件。请告诉我如何在切换到另一个选项卡并返回到旧选项卡后不破坏片段?例如,我有三个选项卡 - A、B、C。我的开始选项卡是 A。导航到 B 后,然后返回 A。当我返回选项卡 A 时,我不希望重新创建它。怎么办呢?谢谢

android navigation android-architecture-navigation
8个回答
40
投票

根据开放问题,导航不直接支持多个返回堆栈 - 即,当您从 A 或 C 返回到 B 时保存堆栈 B 的状态,因为片段不支持多个返回堆栈。

根据此评论

NavigationAdvancedSample 现已在 https://github.com/googlesamples/android-architecture-components/tree/master/NavigationAdvancedSample

提供

此示例使用多个 NavHostFragment,每个底部导航选项卡一个,以解决 Fragment API 当前在支持多个返回堆栈方面的限制。

我们将继续使用 Fragment API 来支持多个返回堆栈,并使用导航 API 在创建后插入其中,这将消除对

NavigationExtensions.kt
文件之类的任何内容的需要。我们将继续使用此问题来跟踪该工作。

因此,您现在可以在应用程序中使用 NavigationAdvancedSample 方法并为问题加注星标,以便在解决根本问题并向导航添加直接支持时获得更新。


7
投票

如果您可以处理销毁片段,但想要保存 ViewModel,您可以将其范围限制到导航图中:

private val viewModel: FavouritesViewModel by 
    navGraphViewModels(R.id.mobile_navigation) {
        viewModelFactory
    }

阅读更多这里

编辑

正如 @SpiralDev 指出的,使用 Hilt 简化了一点:

private val viewModel: MainViewModel by 
    navGraphViewModels(R.id.mobile_navigation) {
         defaultViewModelProviderFactory     
    }

5
投票

只需使用导航组件版本2.4.0-alpha01或以上


1
投票

更新: 使用片段导航组件的最新版本,处理片段状态本身。请参阅此示例

旧:

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
    )
}

您可以对底部导航内的所有片段执行此模式


0
投票

更新

最新版本的导航组件确实支持片段状态处理,但是当您导航到其他片段时,您的片段视图将被销毁,因此您需要将视图保存在变量中以避免页面刷新,请参阅完整答案此处


-1
投票

2021 年更新 使用2.4.0-alpha05或以上版本。 不要使用这个answer或其他等


-4
投票

这可以使用片段显示/隐藏逻辑来实现。

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()
}

-6
投票

在 Activity 上声明 Fragment 并在 onCreate 方法上创建 Fragment 实例,然后在 updateFragment 方法中传递 Fragment 实例。根据需要创建与底部导航侦听器项 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()

© www.soinside.com 2019 - 2024. All rights reserved.