Как сохранить состояние фрагмента при навигации по компоненту навигации - PullRequest
3 голосов
/ 07 января 2020

Я пытаюсь создать приложение с одним действием, используя компоненты архитектуры android. У меня есть фрагмент A, в котором есть несколько текстовых полей. Когда пользователь нажимает кнопку, я перехожу к фрагменту B, где он загружает и редактирует некоторые изображения, после того как приложение переходит обратно к A с помощью кода, подобного следующему:

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

При переходе назад B передает некоторые данные A, используя dataBundle. Проблема в том, что все текстовые поля сбрасываются, потому что фрагмент А в основном воссоздан с нуля. Я где-то читал, что разработчик из Google предлагает, чтобы вы могли просто сохранять представление в переменной, а не раздувать его каждый раз. Итак, попытался сделать это:

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. Что я делаю не так? Как правильно обращаться с подобными делами?

Ответы [ 2 ]

3 голосов
/ 08 января 2020

Я отвечу на ваши вопросы один за другим.

Но это не работает, все текстовые поля сбрасываются при переходе назад к A. Что я делаю не так?

Из FragmentB, когда пользователи заканчивают sh свою работу, и приложение вызывает указанный ниже метод для возврата FragmentA.

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

Вы ожидали, что приложение вернет пользователей во FragmentA, но фактический результат новый FragmentA создан и помещен в вершину заднего стека. Теперь задний стек будет выглядеть следующим образом.

FragmentA (new instance)
FragmentB
FragmentA (old instance)

Вот почему вы видите, что все текстовые поля сброшены, потому что это совершенно новый экземпляр FragmentA.

Какой правильный способ обрабатывать подобные случаи?

Вы хотите запустить фрагмент, а затем получить результат из этого фрагмента, похоже, startActivityForResult метод Activity.

In Android Dev Summit 2019 - Компоненты архитектуры , в 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()
0 голосов
/ 07 января 2020

В основном вам нужно использовать ViewModel для управления жизненным циклом. Затем после проверки это Android Образец архитектуры Расширенные образцы навигации

...