尝试新的体系结构范例,其中presenter创建不可变状态(模型)流和视图只是呈现它。
无法理解如何处理我们只需要一次制作某个事件的情况。有几个例子。
1)笔记应用程序。我们有editText
和saveButton
。用户点击saveButton
,发生了一些处理,应该清除editText。你们能否描述我们ViewState
这里的内容和近似逻辑流程?
我现在看到的问题和陷阱:
editText.textChanges()
。如果我们在text
中有ViewState
并为每个渲染调用渲染它,那么我们将进入递归,因为它将发出新的textChange并将更新状态并再次渲染。text
中的ViewState
才能在方向文本或进程终止/恢复时恢复它,看起来它在这里开箱即用。但想象一下recyclerView
的滚动位置。我们肯定需要保存它才能恢复。我们无法在每次渲染调用时恢复它,因为它看起来很奇怪,不是吗?.doOnNext{ view.clearText() }
它是有道理的,但我们是否参考了规范MVI实现中的视图?正如我所见,莫斯比没有它。 doOnNext
电话的那一刻,观点有可能死亡。 MVI应该帮助我们解决这个问题,但前提是它只是ViewState
的一部分,对吧? 2)Github app。第一个屏幕(组织):orgEditText
,okButton
,progressBar
。第二个屏幕(Repos):recyclerView
。当用户将组织输入orgEditText
并点击okButton
应用时,应向API发出请求,并在成功时导航到Repos屏幕成功或在失败时显示吐司。再次请你为Org屏幕描述ViewState
以及应该是什么样的逻辑?
我现在看到的问题和陷阱:
progressBar
并禁用okButton
。我们应该喜欢加载/内容/错误密封类(让我们称之为ContentState
)并将其实例放在我们的ViewState
中。 View了解如何呈现ContentState.loading
并显示progressBar
并禁用okButton
。我是对的吗? Google建议使用SingleLiveEvent
解决方案,但它看起来很奇怪,然后我们应该有尽可能多的LiveData<SingleLiveEvent>
个流,而不是真正的单一来源。
其他人建议使用render func生成的新意图更好,但有些异步操作可能会再次改变状态,我们将在第一次显示时获得第二个Toast,依此类推。
答案 0 :(得分:5)
1)笔记应用程序:
在一个完美的世界中:是的,只要用户插入文本并呈现,您的ViewState就会发生text
更改。关于递归:我可能错了,但我认为RxBindings提供了一个Observable,它不仅包含已更改的文本,还包含布尔标志,如果此更改是由用户输入或通过编程设置文本引起的。无论如何,我认为如果你检查if (editText.text != viewState.text)
并且仅在文本不同的情况下设置文本,我也可以解决递归问题(请记住,您可能必须使用后续文本被更改时触发的TextWatcher回调启动意图,而不是“在将要改变之前”。)
据说,在Android上我们并不是生活在一个完美的世界。正如您已经说过的,文本将由android自动恢复。因此,不将文本作为ViewState的一部分是有意义的。
所以听起来在这种情况下,ViewState只是一个这样的枚举:
enum ViewState {
// The user can type typing text
IDLING,
// The app is saving the note
PROCESSING,
// After having saved (PROCESSING) the note, CLEARED means, show a new empty note
CLEARED
}
所以初始状态是IDLING
。然后,一旦保存了音符,下一个发出的ViewState就是PROCESSING
。一旦此操作成功,您的业务逻辑会立即触发CLEARED
,然后立即触发IDLING
,最后用户再次看到一个空注,然后可以开始输入新笔记。
请勿使用doOnNext()
来操纵视图。 ViewState是视图的唯一真实来源。
关于RecyclerView:RecyclerView会自动恢复它的滚动位置,否则(在恢复状态后将LayoutManager和/或适配器设置为迟到)。不过,如果你想在ViewState中对滚动位置进行建模,那么在完美的世界中,我认为这是最好的解决方案,你应该考虑不在每个滚动的像素上更新ViewState中的滚动位置,而是在用户做一次不再滚动/ fling已经完成。
2)Github app:
- 我们应该在加载时显示progressBar并禁用okButton。我们 应该像加载/内容/错误密封类(让我们称之为 ContentState)并在ViewState中拥有它的实例。查看知道如何 呈现ContentState.loading并显示progressBar和disables okButton。我是对的吗?
醇>
是
- 如何处理导航呢?
醇>
对我而言,将此作为副作用处理效果很好:我有一个类Navigator
,它被注入到演示者中并在doOnNext { navigator.goToX() }
中使用。然后导航器将其分派给另一个可以临时连接/分离的组件。所以这个其他组件正在观察导航器中的“导航事件”我之所以这样做,那么这个组件就不会泄漏活动/片段上下文。 “这个组件”可以直接是Activity或Fragment或者其他什么,但我倾向于有一个专门的类,让我们称之为Router
,观察导航事件的Navigator
并执行FragmentTransactions
或者您在应用中使用的任何内容。
- 吐司 - 有什么州或我们认为这是副作用?同样的问题。
醇>
可以使用与Snackbar
相同的方法处理此问题(请参阅here)。 Toast没有隐藏Toast的API。因此,您可以立即触发两个ViewState而不是计时器:第一个设置了错误标志(然后导致Toast显示在屏幕上),第二个设置“清除”此标志。像这样:
Observable.just( ViewState(error = true, ...), new ViewState( error = false, ... )
我希望澄清一些事情,但一如既往:不要把它们当成银子弹。做任何最适合您的应用和用例的事情。不要超级宗教,这总是一个案例决定。