该博客参考开源项目:
https://github.com/airbnb/MvRx/wiki
热门教程推荐
Epoxy——RecyclerView的绝佳助手
官方基础教程
https://github.com/airbnb/epoxy/wiki/Epoxy-Controller
配置
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
ext.epoxyVersion = '3.2.0'
implementation("com.airbnb.android:epoxy:$epoxyVersion") { exclude group: 'com.android.support' }
kapt "com.airbnb.android:epoxy-processor:$epoxyVersion"
xml布局
<com.airbnb.epoxy.EpoxyRecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
自定义View
自定义的布局是EpoxyRecyclerView的关键所在,类似于IOS的section
关于Prop请参考
https://github.com/airbnb/epoxy/wiki/Generating-Models-from-View-Annotations
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.CheckBox
import android.widget.FrameLayout
import android.widget.TextView
import com.airbnb.epoxy.CallbackProp
import com.airbnb.epoxy.ModelProp
import com.airbnb.epoxy.ModelView
import com.airbnb.epoxy.TextProp
import com.airbnb.mvrx.todomvrx.todoapp.R
@ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
class TaskItemView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private val clickOverlay by lazy { findViewById<View>(R.id.click_overlay) }
private val checkbox by lazy { findViewById<CheckBox>(R.id.checkbox) }
private val titleView by lazy { findViewById<TextView>(R.id.title) }
init {
inflate(context, R.layout.task_item, this)
}
@TextProp
fun setTitle(title: CharSequence) {
titleView.text = title
}
@ModelProp
fun toChecked(checked: Boolean) {
checkbox.isChecked = checked
setBackgroundResource(if (checked) R.drawable.completed_task_background else 0)
}
@CallbackProp
fun onCheckedChanged(listener: ((Boolean) -> Unit)?) {
if (listener == null) {
checkbox.setOnCheckedChangeListener(null)
} else {
checkbox.setOnCheckedChangeListener { _, isChecked -> listener(isChecked) }
}
}
@CallbackProp
fun onClickListener(listener: View.OnClickListener?) {
clickOverlay.setOnClickListener(listener)
}
}
定义
配合mvRx使操作更简单
class YourFragment : BaseMvRxFragment() {
protected val viewModel by activityViewModel(YoursViewModel::class)
protected val epoxyController by lazy { simpleController() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
epoxyController.onRestoreInstanceState(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
inflater.inflate(R.layout.fragment_base, container, false).apply {
recyclerView.setController(epoxyController)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
epoxyController.onSaveInstanceState(outState)
}
}
fun simpleController(viewModel, YoursViewModel) { state, taskListState ->
// We always want to show this so the content won't snap up when the loader finishes.
horizontalLoader {
id("loader")
loading(state.isLoading)
}
if (state.tasks.isEmpty() && !state.isLoading) {
fullScreenMessageView {
id("empty_message")
iconRes(iconRes)
title(title)
}
} else if (!(state.isLoading && state.tasks.isEmpty())) {
header {
id("header")
title(R.string.label_all)
}
taskItemView {
}
}
}
simpleController具体实现如下
扩展 MvRxEpoxyController.kt
import com.airbnb.epoxy.AsyncEpoxyController
import com.airbnb.epoxy.EpoxyController
import com.airbnb.mvrx.BaseMvRxViewModel
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.todomvrx.core.BaseFragment
import com.airbnb.mvrx.todomvrx.core.MvRxViewModel
import com.airbnb.mvrx.withState
class ToDoEpoxyController(
val buildModelsCallback: EpoxyController.() -> Unit = {}
) : AsyncEpoxyController() {
override fun buildModels() {
buildModelsCallback()
}
}
/**
* Create a [MvRxEpoxyController] that builds models with the given callback.
*/
fun BaseFragment.simpleController(
buildModels: EpoxyController.() -> Unit
) = ToDoEpoxyController {
// Models are built asynchronously, so it is possible that this is called after the fragment
// is detached under certain race conditions.
if (view == null || isRemoving) return@ToDoEpoxyController
buildModels()
}
/**
* 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 {
if (view == null || isRemoving) return@ToDoEpoxyController
com.airbnb.mvrx.withState(viewModel) { state ->
buildModels(state)
}
}
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)
}
}
触发
data class TasksState(
) : MvRxState
setState { copy() }
监听
withState(viewModel) {TaskState ->
}
优点:
1.完美解决复杂页面的问题
2.适配Jetpack
3.开源
缺点:
1.全新的设计模式,代码过于简洁,学习成本较高