24

我有一个应用程序使用 Androids ViewModel 类和导航组件在片段之间导航。我将如何处理来自 ViewModel 的导航?我正在使用 RxJava,我正在考虑让 Fragments 监听导航事件,然后以这种方式触发导航。处理这个的正常方法是什么?如果有帮助,我也会使用 Dagger 进行依赖注入。

4

3 回答 3

19

根据LiveData with SnackBar、Navigation 和其他事件博客文章

一些数据应该只使用一次,例如 Snackbar 消息、导航事件或对话框触发器。

与其尝试通过库或架构组件的扩展来解决这个问题,不如将其作为一个设计问题来面对。我们建议您将您的活动视为您所在州的一部分。

他们详细介绍了SingleLiveEvent类的使用,该类确保每个 Navigation 事件仅由 Observer 接收一次(即,您的 Fragment 可以访问您的NavController)。

另一种选择是使用“事件包装器”模型,其中必须将事件显式标记为已处理。

于 2018-08-16T04:55:56.807 回答
3

如果你使用 MVP,P 只会调用 V 上触发导航的方法。

MVVM 中的等效方法是使用专用的 observable/listener/callback,如果使用 RxJava 完成,它可以由PublishSubject. 这满足一次性要求。相反,如果您需要在订阅之前响应可能发出的事件,您可以使用 aBehaviorSubject<Optional<T>>并创建它createDefault(absent<T>()),使用触发它,onNext(Optional.of(navigationObject))然后让 VM 知道导航发生时,然后 VM 可以使用清除它onNext(absent())

或者,如果你想将它工作到一些包罗万象的 redux/mvi 状态,你可能有一些包含所有状态的状态类,包括一些指示视图导航到某处的属性,在接收/对此采取行动时,视图会告诉VM它已经这样做了,VM会将状态设置为与当前相同但没有导航。例如(在 Kotlin 中)state = state.copy(navigateToX = false)

于 2018-08-18T14:56:31.320 回答
1

对于您的活动,一般来说,您可以使用以下内容:

/**
 * Used as a wrapper for data that is exposed via a LiveData that represents an event.
 */
open class Event<out T>(private val content: T) {

    @Suppress("MemberVisibilityCanBePrivate")
    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
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}

有了这个,你可以很容易地确保你曾经像这样使用过内容:

viewModel.getShowAlertFragment().observe(viewLifecycleOwner, Event{
        event?.getContentIfNotHandled()?.let {
            // your code here
    }
})

但为了让它更简单,实际上避免在任何地方检查内容是否已被使用,您可以创建另一个扩展观察者的类并在那里进行检查:

/**
 * An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has
 * already been handled.
 *
 * [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled.
 */
class EventObserver<T>(private val onEventUnhandledContent: (T) -> Unit) : Observer<Event<T>> {
    override fun onChanged(event: Event<T>?) {
        event?.getContentIfNotHandled()?.let {
            onEventUnhandledContent(it)
        }
    }
}

现在有了这个,你可以将你的代码简化成这样:

viewModel.getShowAlertFragment().observe(viewLifecycleOwner, EventObserver {
        // do you stuff here and you know it will only be called once
    }
})

这个想法来自Android示例中的这个文件Here

于 2020-08-24T15:19:23.873 回答