繁体   English   中英

导航 controller 在 livedata 观察者中被调用两次

[英]Navigation controller gets called twice inside livedata observer

我想要做的是在 LiveData 观察者中使用 Navigation controller,所以当用户从列表中单击一个项目时,它会通知 ViewModel,然后 ViewModel 更新数据,当这种情况发生时,片段会观察到这一点并导航到下一个。

我的问题是,由于某种原因,观察者被调用了两次,第二次我得到一个异常,说这个 NavController 不知道目的地。

我的片段 onCLick:

override fun onClick(view: View?) {
        viewModel.productSelected.observe(viewLifecycleOwner, Observer<ProductModel> {
            try {
                this.navigationController.navigate(R.id.action_product_list_to_product_detail)
            } catch (e: IllegalArgumentException) { }
        })

        val itemPosition = view?.let { recyclerView.getChildLayoutPosition(it) }
        viewModel.onProductSelected(listWithHeaders[itemPosition!!].id)
    }

在我的 ViewModel 中:

fun onProductSelected(productId: String) {
    productSelected.value = getProductById(productId)
}

它被调用了两次,因为首先您订阅,因此您获得了默认值,然后您更改了productSelected LiveData中的值,因此您的观察者再次收到通知。 其中,在调用onProductSelected开始观察,如下所示:

override fun onClick(view: View?) {
    val itemPosition = view?.let { recyclerView.getChildLayoutPosition(it) }
    viewModel.onProductSelected(listWithHeaders[itemPosition!!].id)

    viewModel.productSelected.observe(viewLifecycleOwner, Observer<ProductModel> {
        try {                                
          this.navigationController.navigate(R.id.action_product_list_to_product_detail)
            } catch (e: IllegalArgumentException) { }
    })
}

再次注意,一旦您开始观察LiveData ,每次productSelected更改时都会收到通知。 是你想要的吗? 如果没有,那么您应该在观察者使用一次后将其移除。

捕获异常可能会起作用,但它也会让您错过其他几个问题。 最好用目的地检查当前布局以验证用户是否已经在那里。 我更喜欢的另一种选择是检查以前的目的地,例如:

fun Fragment.currentDestination() = findNavController().currentDestination

fun Fragment.previousDestination() = findNavController().previousBackStackEntry?.destination

fun NavDestination.getDestinationIdFromAction(@IdRes actionId: Int) = getAction(actionId)?.destinationId

private fun Fragment.isAlreadyAtDestination(@IdRes actionId: Int): Boolean {
    val previousDestinationId = previousDestination()?.getDestinationIdFromAction(actionId)
    val currentDestinationId = currentDestination()?.id
    return previousDestinationId == currentDestinationId
}

fun Fragment.navigate(directions: NavDirections) {
    if (!isAlreadyAtDestination(directions.actionId)) {
        findNavController().navigate(directions)
    }
}

基本上,我们在这里验证我们尚未到达目的地。 这可以通过将先前的操作目标与当前目标进行比较来完成。 让我知道代码是否有帮助!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM