前言
Android真響應式架構(gòu)系列文章:
Android真響應式架構(gòu)——MvRx
Epoxy——RecyclerView的絕佳助手
Android真響應式架構(gòu)——Model層設計
Android真響應式架構(gòu)——數(shù)據(jù)流動性
Android真響應式架構(gòu)——Epoxy的使用
Android真響應式架構(gòu)——MvRx和Epoxy的結(jié)合
之前的文章講了MvRx,講了Epoxy,但是卻沒有講MvRx是怎么跟Epoxy結(jié)合起來的。這篇文章就來講講MvRx和Epoxy是怎么結(jié)合在一起進而實現(xiàn)數(shù)顯兩開花的(前面都省略掉,不管了直接開花吧)。
這是這個系列文章的最后一篇了,本意是想寫一篇MvRx在一個復雜界面上的實踐的,但是,我發(fā)現(xiàn)這個實例有點復雜,并且牽扯到過多的業(yè)務細節(jié),很難講清楚,所以就放棄了。
1. MvRx和Epoxy結(jié)合
在MvRx的sample中已經(jīng)展示了如何使用Epoxy,還是很簡單的。
abstract class BaseFragment : BaseMvRxFragment() {
//Epoxy擴展的 RecyclerView,第二篇文章中講過
protected lateinit var recyclerView: EpoxyRecyclerView
protected val epoxyController by lazy { epoxyController() }
//ViewModel中 State的任意改變都會調(diào)用該方法,這里也僅僅是通知 EpoxyController重建 Model
override fun invalidate() {
recyclerView.requestModelBuild()
}
abstract fun epoxyController(): ToDoEpoxyController
}
class ToDoEpoxyController(
val buildModelsCallback: EpoxyController.() -> Unit = {}
) : AsyncEpoxyController() { //AsyncEpoxyController,異步的 EpoxyController,Model的構(gòu)建及diff均在子線程中,第二篇文章中講過
override fun buildModels() {
//委托給我們傳遞進來的 EpoxyController的擴展函數(shù)
buildModelsCallback()
}
}
代碼很簡單,關鍵就是如何在子類中實現(xiàn)buildModelsCallback
。繼續(xù)往下看:
/**
* Create a [ToDoEpoxyController] that builds models with the given callback.
* When models are built the current state of the viewmodel will be provided.
* 嗯,你看人家說的多好,就是這個意思。。。
*/
fun <S : MvRxState, A : MvRxViewModel<S>> BaseFragment.simpleController(
viewModel: A,
buildModels: EpoxyController.(state: S) -> Unit
) = ToDoEpoxyController {
//構(gòu)建Models是異步的,可能fragment已經(jīng)不存在
if (view == null || isRemoving) return@ToDoEpoxyController
//MvRx給我們提供的 withState方法,線程安全,可以獲取當前的 State,便于我們構(gòu)建Models
withState(viewModel) { state ->
buildModels(state)
}
}
//兩個ViewModel,如果你需要更多個ViewModel,可以繼續(xù)擴展
fun <A : BaseMvRxViewModel<B>, B : MvRxState, C : BaseMvRxViewModel<D>, D : MvRxState> BaseFragment.simpleController(
viewModel1: A,
viewModel2: C,
buildModels: EpoxyController.(state1: B, state2: D) -> Unit
) = ToDoEpoxyController {
if (view == null || isRemoving) return@ToDoEpoxyController
withState(viewModel1, viewModel2) { state1, state2 ->
buildModels(state1, state2)
}
}
/**
* 具體某個Fragment的實現(xiàn)
*/
class TaskListFragment : BaseFragment() {
private val taskListViewModel: TaskListViewModel by fragmentViewModel()
override fun epoxyController() = simpleController(viewModel, taskListViewModel) { state, taskListState ->
//Epoxy生成的EpoxyModel,使用Kotlin的情況下可以采用這種DSL的寫法
horizontalLoader {
id("loader")
loading(state.isLoading)
}
//...
}
}
在simpleController
的幫助下,獲取當前State,并且重建EpoxyModels即可。Epoxy負責界面的響應式,MvRx實現(xiàn)狀態(tài)的管理,雙劍合璧,相得益彰。
2. 顯示Loading
MvRx給的sample中,Loading也是作為RecyclerView的一個item管理的。這固然是為了界面管理的統(tǒng)一,但是又有點矯枉過正了。一般情況下,我們還是希望Loading以浮動的形式呈現(xiàn)。前文提到MvRx使用Async
來表示數(shù)據(jù)的加載狀態(tài),我們可以通過觀察State中的Async
屬性來顯示Loading。以下是我的實踐:
//定義State的基類,所有State都包含屬性repoAsync
interface BaseState : MvRxState {
val repoAsync: Async<Any> //僅僅是為了顯示Loading,泛型指定為Any
}
abstract class BaseViewModelFragment<S : BaseState, VM : BaseMvRxViewModel<S>> : BaseMvRxFragment() {
//主ViewModel,F(xiàn)ragment可以包含有多個View,但是這個ViewModel有顯示Loading的作用
protected abstract val viewModel: VM
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.subscribe { state ->
if (state.repoAsync is Loading) {
//顯示Loading
startLoading()
} else if (state.repoAsync.complete) { //Async包含complete屬性,Success或者Fail的情況下為 true
//取消Loading
stopLoading()
if (state.repoAsync is Fail) {
//處理失敗信息
handleFail((state.repoAsync as Fail<Any>).error)
}
}
}
}
}
data class SomeState(...) : BaseState
class SomeViewModel(initialState: SomeState, private val repo: DataRepository) : BaseMvRxViewModel<SomeState>(initialState) {
fun fetchData() {
repo.fetchData() //假設返回的是Observable
.excute {
//把it(即Async)賦值給repoAsync就會顯示Loading;不需要顯示Loading,不賦值即可
copy(repoAsync = it, ...)
}
}
}
如上,通過State基類中的repoAsync
屬性,統(tǒng)一控制Loading的顯示。需要顯示Loading的請求,對repoAsync
賦值即可(前提得是主ViewModel);不需要顯示Loading,不賦值即可。
總結(jié)
沒啥好總結(jié)的,一目了然。好吧,這一系列文章終于寫完了,歡迎留言與我交流。