【问题标题】:Android Jetpack Navigation Fragment show again and againAndroid Jetpack Navigation Fragment 一次次展示
【发布时间】:2021-08-04 17:19:15
【问题描述】:

我正在使用 Jetpack 库开发一个安卓应用:

  • 刀柄
  • 导航
  • 视图模型
  • 数据绑定

其实我对MVP模式很熟悉。

我正在尝试研究 MVVP 模式(数据绑定和 Jetpack ViewModel)

我有 2 个片段(A 和 B)。

import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs

@AndroidEntryPoint
class AFragment {

    private val viewModel: AViewModel by viewModels()

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

        binding.viewModel = viewModel

    with(binding) {
        button.setOnClickListener {
            this@AFragment.viewModel.doAction()
        }
    }

    viewModel.result.observe(viewLifecycleOwner) { result ->
        findNavController().navigate(AFragmentDirections.actionAFragmentToBFragment(result))
    }
  }
}

这里是 AViewModel:

@HiltViewModel
class AViewModel @Inject constructor(): ViewModel() {

    private val _result: MutableLiveData<Int> = MutableLiveData()
    val result: LiveData<Int>
        get() = _result

    fun doAction() {
        _result.postValue(SOME_ACTION_RESULT)
    }
}

它正确显示了 BFragment。 但是如果我在 BFragment 上触摸Back Button,它仍然会显示 BFragment。 实际上,它回到了AFragment,但又回到了BFragment。

当我在 BFragment 上触摸 Back Button 时,

  1. AFragment 再次启动(我检查过 onViewCreated() 是否被再次调用)
  2. 再次调用以下观察代码:
viewModel.result.observe(viewLifecycleOwner) { result ->
    findNavController().navigate(AFragmentDirections.actionAFragmentToBFragment(result))
}

为什么会再次调用这段代码?

我是否正确编写代码?

最佳做法是什么?


现在,我找到了解决办法。

在 A 片段中:

viewModel.result.observe(viewLifecycleOwner) { result ->
    if (result != null) {
        findNavController().navigate(AFragmentDirections.actionAFragmentToBFragment(result))
        viewModel.resetResult()
    }
}

在 AViewModel 中:

fun resetResult() {
    _result.postValue(null)
}

使用此代码,它可以正常工作。

是的...但是我不喜欢这段代码...

这……太奇怪了……

我不知道什么是最佳实践...

【问题讨论】:

  • 在结果上放一个断点来检查当你按下返回时它是否改变了。每次更改时,都会调用导航到另一个片段的代码。如果是这种情况,为什么不使用布尔值来确定它是否应该导航?
  • doAction() 函数只被调用一次,永远不会再被调用。

标签: android mvvm android-databinding android-viewmodel android-navigation


【解决方案1】:

问题与 livedata 和片段生命周期有关。

当您移动到 ​​FragmentB 时,AFragment 和 AViewModel 仍然存在,但是当您移动并返回时,AFragment 中的视图会分离并附加。这意味着每次触摸 BFragment 上的后退按钮时都会调用 onViewCreated()。结果,AFragment 开始观察 AViewModel,它的_result livedata 已经有有效数据。

你应该在 livedatas 中分离 uidata 和 events。最简单的解决方案是 SingleEventLiveData 实现并使用它。

  open class Event<out T>(private val content: T) {

  var hasBeenHandled = false
    private set // Allow external read but not write

  /**
   * Returns the content and prevents its use again.
  */
  fun getContentIfNotHandled(): T? {
    return if (hasBeenHandled) {
      null
    } else {
      hasBeenHandled = true
      content
    }
  }

在视图模型中:

private val _result: MutableLiveData<Event<Int>> = MutableLiveData()
val result: LiveData<Event<Int>>
    get() = _result

fun doAction() {
    _result.postValue(Event(5))
}

如何观察:

viewModel.result.observe(viewLifecycleOwner) { result ->
result.getContentIfNotHandled()?.let {
    findNavController().navigate(R.id.action_fragment_a_to_fragment_b)
}

}

来源: https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150

How to create LiveData which emits a single event and notifies only last subscribed observer?

【讨论】:

    猜你喜欢
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多