前言
在MVP模式下,随着业务逻辑的不断增加,UI也会变得越来的多样化,UI改变多样化势必会造成View层的接口case变多,View的接口类就会变得异常庞大。MVVM的方式完美的解决了MVP模式的这一痛点,它采用双向绑定的机制,实现了UI与数据的绑定操作。仅通过改变数据就可以实现对UI的更新操作。
DataBinding
MVVM在前端领域可谓是如鱼得水,很多著名的框架都是采用的MVVM模式,最近大火的VUE就是其中之一,而在安卓领域,由于一直一来xml的layout都是一个比较功能弱化的存在,所以实现MVVM双向绑定就变得比较困难,直到谷歌推出了实现视图和数据双向绑定的工具DataBinding,才让双向绑定变得操作简单。
初始化
在项目中开启DataBinding只需要在gradle的android下新增以下代码即可:
dataBinding {
enabled = true
}
在xml的编写上,DataBinding有新的写法,最外层不再是页面的根布局,而是layout,layout中包含有生命的绑定class以及根布局。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<!--绑定数据类-->
<variable name="weatherData"
type="com.yanghaoyi.klivedata.model.bean.WeatherNetData"/>
<!--绑定逻辑类-->
<variable
name="presenter"
type="com.yanghaoyi.klivedata.view.MainActivity.WeatherPresenter"/>
</data>
<!--页面根布局-->
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
</layout>
编写完xml文件后需要rebuild一下工程,rebuild成功后会自动生成继承ViewDataBinding的绑定Class类,是通过layout文件进行命名的,例如activity_main.xml生成的绑定class为ActivityMainBinding。ActivityMainBinding需要在onCreate函数中通过setContentView进行初始化。
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//初始化DataBinding
binding =DataBindingUtil.setContentView(this,R.layout.activity_main)
binding.presenter = WeatherPresenter()
init()
}
建立双向绑定
建立了绑定机制后在xml文件中即可通过使用@{}实现View与Data的双向绑定,以TextView为例,当weatherData.data.ganmao放生改变的时候,tvCold文本组件会自动更新文本显示。??相当于?:表达式,即当weatherData.data.ganmao数据为null的时候,文本组件取值为 ' 详情 '。
<TextView
android:id="@+id/tvCold"
style="@style/Common_Button"
android:text="@{weatherData.data.ganmao??`详情`}"/>
除了简单的数据绑定,DataBinding还可以为我们实现事件的绑定,以点击按钮从EditText获取文字查询为例,首先定义事件Presenter绑定,binding.presenter = WeatherPresenter()并在xml中建立variable对应关系,WeatherPresenter内部逻辑非常简单,如下:
/** 点击事件逻辑中心 **/
open inner class WeatherPresenter{
open fun onClick(view:View,city:String){
when (view.id) {
R.id.tvSearch -> {
this@MainActivity.viewModel.requestWeather(city)
}
}
}
}
WeatherPresenter的onClick函数需要两个参数,一个是被点击的view,一个是字符串类型的城市名称,在xml中我们只需要@{(view)->presenter.onClick(view,edInput.text.toString)}即可实现tvSearch控件与WeatherPresenter的绑定。
<EditText
android:id="@+id/edInput"
android:hint="@string/please_input_city"
style="@style/Common_EditText"/>
<View
style="@style/Common_CutLine"/>
<TextView
android:id="@+id/tvSearch"
style="@style/Common_Button"
android:text="@string/search"
android:onClick="@{(view)->presenter.onClick(view,edInput.text.toString)}"/>
LiveData
LiveData 是一个能够感知 Activity 与 Fragment生命周期的数据容器。当 LiveData 所持有的数据改变时,它会通知相应的界面代码进行更新。LiveData 持有界面代码 Lifecycle 的引用,只有在界面生命周期处于 Started 或 Resumed状态时才会更新数据。当绑定对象销毁,LiveData会自行清理。
结合ViewModel
ViewModel是业务逻辑的控制中心,当数据Data放生改变的时候,viewModel更新LiveData的value值,由LiveData的双向绑定机制通知UI更新。
/** 添加viewModel绑定 **/
private fun init(){
viewModel = ViewModelProvider(
this, ViewModelProvider.AndroidViewModelFactory(application)
).get(WeatherViewModel::class.java)
//添加数据观察
viewModel.weatherEvent.observe(this, Observer<WeatherNetData> { data -> binding.weatherData = data })
}
在LiveData中并没有对外暴露的公共方法进行更新数据,可以使用MutableLiveData进行数据的更新,分别是setValue和postValue,他们之间的区别在于setValue是必须是在主线程的,而postValue可以是在子线程中使用。
/** LiveData初始化 **/
var weatherEvent = MutableLiveData<WeatherNetData>()
/** 获取天气数据 **/
open fun requestWeather(city:String){
WeatherModel().request(object :OnGetDataListener<WeatherNetData>{
override fun fail(response: WeatherNetData?, msg: String?) {
//数据更新
//主线程
weatherEvent.value = response
//子线程
weatherEvent.postValue(response)
}
override fun success(response: WeatherNetData?) {
//数据更新
weatherEvent.value = response
}
},city)
}
MVVM实例
为了更好的展示DataBinding与LiveData实现MVVM的模式,这里用一个简单小例子来辅助说明一下。
首先我们来明确一下Demo的功能,这是一个通过输入城市名查询城市天气的简单应用,在MVVM划分中,Model层应当包含网络请求以及网络请求的数据Bean,View层应当包括一个EditText控件和两个TextView控件,ViewModel中通过LiveData实现数据与视图的双向绑定。
Model
Model层内部是一个网络请求,通过接口回调将请求结果返回给ViewModel,如下:
/**
* @author : YangHaoYi on 2019/6/4.
* Email : yang.haoyi@qq.com
* Description : 数据Model
* Change : YangHaoYi on 2019/6/4.
* Version : V 1.0
*/
class WeatherModel : AbstractBaseModel() {
/** 天气网络请求 **/
fun request(listener:OnGetDataListener<WeatherNetData>,city:String){
val response = api.getWeatherData(city)
response.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(object :Observer<WeatherNetData>{
override fun onError(e: Throwable?) {
listener.fail(null,"")
}
override fun onNext(t: WeatherNetData?) {
listener.success(t)
}
override fun onCompleted() {
}
})
}
}
ViewModel
ViewModel层内部通过LiveData实现对View数据改变的通知
open class WeatherViewModel : ViewModel(){
var weatherEvent = MutableLiveData<WeatherNetData>()
/** 获取天气数据 **/
open fun requestWeather(city:String){
WeatherModel().request(object :OnGetDataListener<WeatherNetData>{
override fun fail(response: WeatherNetData?, msg: String?) {
//主线程
weatherEvent.value = response
}
override fun success(response: WeatherNetData?) {
weatherEvent.value = response
}
},city)
}
}
View
View中收到更新状态后通过DataBinding实现对UI的更新
class MainActivity : FragmentActivity() {
lateinit var binding: ActivityMainBinding
lateinit var viewModel:WeatherViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//初始化DataBinding
binding =DataBindingUtil.setContentView(this,R.layout.activity_main)
binding.presenter = WeatherPresenter()
init()
}
/** 添加viewModel绑定 **/
private fun init(){
viewModel = ViewModelProvider(
this, ViewModelProvider.AndroidViewModelFactory(application)
).get(WeatherViewModel::class.java)
//添加数据观察
viewModel.weatherEvent.observe(this, Observer<WeatherNetData> { data -> binding.weatherData = data })
}
}
示例源码
文章对通过DataBinding与LiveData实现的MVVM功能展示Demo已上传至GitHub,感兴趣的朋友可以clone下来共同探讨学习一下。
GitHub源码:Java版本链接