Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
735 views
in Technique[技术] by (71.8m points)

android - How to save fragment state while navigating with navigation component

I'm trying to create a single activity app using android architecture components. I have a fragment A which has some textfields, when user pushes a button I navigate to fragment B where he uploads and edits some images after that app navigates back to A using code like this:

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

When navigating back B passes some data to A using dataBundle. The problem with this is all the textfields reset because Fragment A is basically recreated from scratch. I read somewhere that a developer from google suggests that you can just save view in a var instead of inflating it everytime. So tried doing that:

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

But this does not work, all the textfields reset when navigating back to A. What am I doing wrong? What is proper way of handling cases like this?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I will answer your questions one by one.

But this does not work, all the textfields reset when navigating back to A. What am I doing wrong?

From FragmentB, when users finish their work and the app call the below method to return FragmentA.

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

You expected that the app will bring users back to FragmentA, but the actual result is a new FragmentA is created and put on the top of the back stack. Now the back stack will be like this.

FragmentA (new instance)
FragmentB
FragmentA (old instance)

That why you see all textfields reset, because it is a totally new instance of FragmentA.

What is proper way of handling cases like this?

You want to start a fragment, then receive result form that fragment, it seems like startActivityForResult method of Activity.

In Android Dev Summit 2019 - Architecture Components, at 2:43, there is a question for Android developers.

Can we have something like startFragmentForResult for the Navigation Controller?

The answer is they are working on it, and this feature will be available in future.

Back to your problem, here is my solution.

Step 1: Create a class called SharedViewModel

class SharedViewModel : ViewModel() {

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

Step 2: Add these lines of code to 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()
    })
}

Step 3: Add these lines of code to 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()

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...