MVVM模式的理解

MVVM模式是指Model-View-ViewModel,该模式中的View是将View的状态和行为完全抽象化,把逻辑与界面的控制交给ViewModel处理
Model 用于获取业务数据模型
View 定义了界面中的布局和外观
ViewModel 逻辑控制层,负责处理数据和处理View层中的业务逻辑
当Jetpack遇上MVVM-LMLPHP



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>
08-30 17:16