MVVM模式的理解
MVVM模式是指Model-View-ViewModel,该模式中的View是将View的状态和行为完全抽象化,把逻辑与界面的控制交给ViewModel处理
Model 用于获取业务数据模型
View 定义了界面中的布局和外观
ViewModel 逻辑控制层,负责处理数据和处理View层中的业务逻辑
MVVM优缺点
优点
(1)双向绑定技术,当Model变化时,View也会自动变化,做到数据的一致性
(2)View的功能进一步强化,具有控制的部分功能,由于控制器的功能部分移动到View上处理,从而对控制器进行了瘦身
(3)可以对View或ViewController的数据处理部分抽象出一个函数处理model,这样它们专职页面布局和页面跳转,必然是进一步的简化
缺点
(1)数据绑定使得 Bug 难以调试,当你看到界面异常了,有可能是你 View 的代码有问题,也可能是 Model 的代码有问题
(2)数据双向绑定不利于代码重用,一个View绑定了一个model,不同模块的model不同,那就不能简单重用View了
Model层
package com.example.myapplication
data class QuestionBean(
val `data`: Data,
val errcode: String,
val errorMsg: String
)
data class Data(
val answerData: AnswerData,
val hasAnswer: Boolean,
val options: List<Option>,
val questionData: QuestionData
)
data class AnswerData(
val answer: String,
val id: String,
val isright: String,
val optionid: String,
val point: String,
val questionid: String,
val remark: Any,
val resultid: String
)
data class Option(
val id: String,
val title: String
)
data class QuestionData(
val isbank: String,
val paperid: String,
val point: String,
val questionId: String,
val questionTypeName: String,
val questiontype: String,
val title: String,
val useTime: String
)
Repository层
这里使用的是RxHttp,github地址:https://github.com/liujingxing/okhttp-RxHttp
class MainRepository {
fun getData(liveData: MutableLiveData<QuestionBean>) {
RxHttp.postForm(MainApi.URL)
.add("questionId", "139")
.add("resultId", "143")
.asClass(QuestionBean::class.java)
.observeOn(AndroidSchedulers.mainThread())//指定回调线程
.subscribe({ question: QuestionBean? ->
liveData.value = question
}) { throwable: Throwable? ->
Log.d(TAG, throwable?.message.toString())
}
}
}
ViewModel层
class QuestionViewModel : ViewModel() {
private var mainRepository: MainRepository? = null
private var liveData: MutableLiveData<QuestionBean>? = null
init {
mainRepository = MainRepository()
}
fun getLiveData(): MutableLiveData<QuestionBean>? {
if (liveData == null) {
liveData = MutableLiveData()
}
return liveData
}
fun getDataFromNetWork() {
mainRepository?.getData(liveData!!)
}
}
View层
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="test"
type="com.example.myapplication.QuestionBean" />
</data>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{test.data.questionData.title}" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</layout>
Activity
class MainActivity : AppCompatActivity() {
private lateinit var activityMainBinding: ActivityMainBinding
private val viewModel by lazy {
ViewModelProvider(
this,
ViewModelProvider.NewInstanceFactory()
).get(QuestionViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
val liveData = viewModel.getLiveData()
initData()
activityMainBinding.recyclerview.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
liveData?.observe(this, {
activityMainBinding.refresh.isRefreshing = false
activityMainBinding.setVariable(BR.test, it)
activityMainBinding.recyclerview.adapter = TestAdapter(it.data.options, this)
})
activityMainBinding.lifecycleOwner = this
activityMainBinding.refresh.setOnRefreshListener {
initData()
}
}
private fun initData() {
activityMainBinding.refresh.isRefreshing = true
viewModel.getDataFromNetWork()
}
}
相应的Adapter
class TestAdapter(val data: List<Option>, val context: Context) :
RecyclerView.Adapter<TestAdapter.ItemHolder>() {
class ItemHolder(var viewDataBinding: ViewDataBinding) :
RecyclerView.ViewHolder(viewDataBinding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHolder {
val itemBinding = DataBindingUtil.inflate<ViewDataBinding>(
LayoutInflater.from(context),
R.layout.item,
parent,
false
)
return ItemHolder(itemBinding)
}
override fun onBindViewHolder(holder: ItemHolder, position: Int) {
holder.viewDataBinding.setVariable(BR.options, data[position])
holder.viewDataBinding.executePendingBindings()
holder.viewDataBinding.root.setOnClickListener {
Toast.makeText(context, "点击了第${position}个", Toast.LENGTH_SHORT).show()
}
}
override fun getItemCount(): Int {
return data.size
}
}
item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="options"
type="com.example.myapplication.Option" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
text="@{options.title}"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>