android-fragments 如何在使用导航组件进行导航时保存片段状态

w9apscun  于 2022-11-13  发布在  Android
关注(0)|答案(8)|浏览(219)

我正在尝试使用Android架构组件创建一个单Activity应用。我有一个片段A,其中包含一些文本字段,当用户按下按钮时,我导航到片段B,在那里他上传并编辑一些图像,然后应用使用如下代码导航回片段A:
findNavController().navigate(R.id.action_from_B_to_A, dataBundle)
当导航返回时,B使用dataBundle将一些数据传递给A。问题是所有的文本字段都重置了,因为片段A基本上是从头重新创建的。我在某个地方读到过google的一个开发人员建议你可以只将视图保存在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
    }
}

但这不起作用,当导航回A时,所有文本字段都重置了。我做错了什么?处理这种情况的正确方法是什么?

yks3o0rb

yks3o0rb1#

我会一一回答你的问题。
但这不起作用,当导航回A时,所有文本字段都会重置。我做错了什么?
在FragmentB中,当用户完成工作时,应用调用下面的方法返回FragmentA。

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

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

FragmentA (new instance)
FragmentB
FragmentA (old instance)

这就是为什么你会看到所有的文本字段都被重置了,因为这是FragmentA的一个全新的示例。
怎样处理这类案件才是正确的?
您想要启动一个片段,然后从该片段接收结果,它看起来像Activity的startActivityForResult方法。
Android Dev Summit 2019 - Architecture Components,at 2:43,有一个问题要问Android开发人员。
导航控制器是否可以使用类似startFragmentForResult的内容?
答案是他们正在努力,这个功能将在未来提供。
回到你的问题上,这是我的解决方案。

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

class SharedViewModel : ViewModel() {

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

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

**步骤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()
plupiseo

plupiseo2#

只是一件小事,如果你的观点不具有身份证的话,国家不会保留!
视图需要一个ID来保留其状态。此ID在片段及其视图层次结构中必须是唯一的。没有ID的视图无法保留其状态。
看看谷歌官方文档查看状态

lsmepo6l

lsmepo6l3#

如果你想在弹出后保持滚动视图的位置,确保你把ID放在NestedScrollView这样的视图中,即使它在代码级别上是不需要的:

android:id="@+id/some_id"

这样,当Fragment从反向堆栈返回时,滚动位置保持不变。

5lhxktic

5lhxktic4#

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

private var mRootView: ViewGroup? = null
private var mIsFirstLoad = false

在片段A的onCreateView()中,我写入

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

    if (mRootView == null) {
        mRootView = _binding?.root
        mIsFirstLoad = true
    } else {
        mIsFirstLoad = false
    }
    return mRootView

在片段A的onViewCreated()中,我检查了“mIsFirstLoad”的值

if(mIsFirstLoad) {
    initAdapter()
    getMovies()
} else {
    //Continue with previously initialised variables. e.g. adapter   
}
zed5wv10

zed5wv105#

检查Google文档的link
如果你的操作是fragmentA -〉fragmentB -〉fragmentC,那么从fragmentC回到fragmentA,你想删除fragmentC,fragmentB,并保持fragmentA的状态。
因此,您应该:
1.将动作从片段C添加到片段A
1.将“popUpTo”设置为fragmentA并且将popUpToInclusive设置为真。

c90pui9n

c90pui9n6#

我找到了一个替代方案,我只是清除了所有的backstack,并前往privious目的地与saveState真,这对我的工作

fragmentOtpBinding.verifyOtpButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Navigation.findNavController(view).popBackStack(R.id.destinationFragmentId,false,true);
        }
    });

如果您在popBackStack()方法中也执行包含,则您的应用程序将崩溃(当您想要转到其他目标时),并显示错误“无法从当前目标NavGraph中找到操作”

2exbekwf

2exbekwf7#

你可以使用base fragment,但这只是一个变通方法。实际上,导航组件仍然有bug。下面是GitHub上的一个issue。示例:

class SearchFragment : BaseBottomTabFragment() {

    private var _binding: FragmentSearchBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        _binding = FragmentSearchBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.buttonDynamicTitleNavigate.setOnClickListener {
            navigateWithAction(
                SearchFragmentDirections.actionSearchFragmentToDynamicTitleFragment(
                    binding.editTextTitle.text.toString()
                )
            )
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

代码从以下位置截取:This project

unftdfkk

unftdfkk8#

您需要创建作用于导航图的ViewModel。
检查这个不错的指南,你可以保留你的导航组件。它非常容易实现,对我来说很有效!!https://medium.com/sprinthub/a-step-by-step-guide-on-how-to-use-nav-graph-scoped-viewmodels-cf82de4545ed

相关问题