如何在使用导航组件导航时保存片段状态

Amo*_*kar 23 android android-fragments kotlin

我正在尝试使用 android 架构组件创建单个活动应用程序。我有一个片段 A,它有一些文本字段,当用户按下一个按钮时,我导航到片段 B,在该应用程序使用如下代码导航回 A 后,他上传并编辑一些图像:

findNavController().navigate(R.id.action_from_B_to_A, dataBundle)

当导航回 B 时,使用dataBundle. 问题在于所有文本字段都重置了,因为片段 A 基本上是从头开始重新创建的。我在某处读到谷歌的开发人员建议您可以将视图保存在 var 中,而不是每次都膨胀它。所以尝试这样做:

private var savedViewInstance: View? = null

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
): View? {
    return if (savedViewInstance != null) {
        savedViewInstance
    } else {
        savedViewInstance =
                inflater.inflate(R.layout.fragment_professional_details, container, false)
        savedViewInstance
    }
}
Run Code Online (Sandbox Code Playgroud)

但这不起作用,所有文本字段在导航回 A 时都会重置。我做错了什么?处理此类案件的正确方法是什么?

Nhấ*_*ang 12

我会一一回答你的问题。

但这不起作用,所有文本字段在导航回 A 时都会重置。我做错了什么?

在 FragmentB 中,当用户完成工作并且应用调用以下方法返回 FragmentA。

findNavController().navigate(R.id.action_from_B_to_A, dataBundle)
Run Code Online (Sandbox Code Playgroud)

您期望该应用程序会将用户带回 FragmentA,但实际结果是创建了一个新的 FragmentA 并将其放在返回堆栈的顶部。现在返回堆栈将是这样的。

FragmentA (new instance)
FragmentB
FragmentA (old instance)
Run Code Online (Sandbox Code Playgroud)

这就是为什么您会看到所有文本字段都重置的原因,因为它是 FragmentA 的全新实例。

处理此类案件的正确方法是什么?

您想启动一个片段,然后从该片段接收结果,这似乎startActivityForResult是 Activity 的方法。

Android开发高峰论坛2019 -体系结构组件,在2:43,有一个Android开发的问题。

我们可以startFragmentForResult为导航控制器提供类似的东西吗?

答案是他们正在努力,这个功能将在未来可用。

回到你的问题,这是我的解决方案。

第 1 步:创建一个名为的类SharedViewModel

class SharedViewModel : ViewModel() {

    // This is the data bundle from fragment B to A
    val bundleFromFragmentBToFragmentA = MutableLiveData<Bundle>()
}
Run Code Online (Sandbox Code Playgroud)

第 2 步:将这些代码行添加到 FragmentA

private lateinit var viewModel: SharedViewModel

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    viewModel = ViewModelProviders.of(requireActivity()).get(SharedViewModel::class.java)
    viewModel.bundleFromFragmentBToFragmentA.observe(viewLifecycleOwner, Observer {
        // This will execute when fragment B set data for `bundleFromFragmentBToFragmentA`
        // TODO: Write your logic here to handle data sent from FragmentB
        val message = it.getString("ARGUMENT_MESSAGE", "")
        Toast.makeText(requireActivity(), message, Toast.LENGTH_SHORT).show()
    })
}
Run Code Online (Sandbox Code Playgroud)

第 3 步:将这些代码行添加到 FragmentB

// 1. Declare this as class's variable
private lateinit var viewModel: SharedViewModel

// 2. Use the following code when you want to return FragmentA           
// findNavController().navigate(R.id.action_from_B_to_A) // Do not use this one

// Set data for `bundleFromFragmentBToFragmentA`
val data = Bundle().apply { putString("ARGUMENT_MESSAGE", "Hello from FragmentB") }
viewModel.bundleFromFragmentBToFragmentA.value = data

// Pop itself from back stack to return FragmentA
requireActivity().onBackPressed()
Run Code Online (Sandbox Code Playgroud)


Jim*_*era 11

如果您想在弹出后保留滚动视图的位置,请确保将 ID 放入视图中,即使NestedScrollView在代码级别不需要:

android:id="@+id/some_id"
Run Code Online (Sandbox Code Playgroud)

这样,当Fragment从返回堆栈返回时,滚动位置将被保留。


Ali*_*rei 10

只是一件小事,如果你的视图没有 id,状态将不会被保留!

视图需要一个 ID 来保留其状态。该 ID 在片段及其视图层次结构中必须是唯一的。没有 ID 的视图无法保留其状态。

看一下google官方文档查看状态

  • 愿意详细说明吗? (3认同)

小智 8

在frag A中,我创建了两个全局变量

private var mRootView: ViewGroup? = null
private var mIsFirstLoad = false
Run Code Online (Sandbox Code Playgroud)

在frag A的onCreateView()中,我写

_binding = FragmentDashBoardBinding.inflate(inflater, container, false)

    if (mRootView == null) {
        mRootView = _binding?.root
        mIsFirstLoad = true
    } else {
        mIsFirstLoad = false
    }
    return mRootView
Run Code Online (Sandbox Code Playgroud)

在frag A的onViewCreated()中,我检查“mIsFirstLoad”的值

if(mIsFirstLoad) {
    initAdapter()
    getMovies()
} else {
    //Continue with previously initialised variables. e.g. adapter   
}
Run Code Online (Sandbox Code Playgroud)