一手遮天 Android - jetpack: DataBinding(MVVM)
一手遮天 Android - jetpack: DataBinding(MVVM)
示例如下:
/jetpack/lifecycle/DataBindingDemo.kt
/**
* DataBinding 就是 android 官方的 MVVM 模式开发框架
*
* 如需启用 DataBinding 请在 build.gradle 的 android {} 中做如下配置
* dataBinding {
* enabled = true;
* }
* 如需使用 DataBinding 相关注解,则需要在 gradle 中配置 apply plugin: 'kotlin-kapt'
* 注:DataBinding 需要 gradle 根据你的配置生成一些类,如果遇到问题就 Rebuild Project 重新生成一下
*/
package com.webabcd.androiddemo.jetpack.lifecycle
import android.os.Bundle
import android.util.Log
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.*
import androidx.databinding.Observable
import androidx.databinding.library.baseAdapters.BR
import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide
import com.webabcd.androiddemo.MyApplication
import com.webabcd.androiddemo.databinding.ActivityJetpackLifecycleDatabindingdemoBinding
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.*
class DataBindingDemo : AppCompatActivity() {
// ActivityJetpackLifecycleDatabindingdemoBinding 是 gradle 生成的类,其对应的 xml 是 activity_jetpack_lifecycle_databindingdemo.xml
private lateinit var mBinding: ActivityJetpackLifecycleDatabindingdemoBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 设置 activity 的布局,并获取绑定对象(方式一)
// binding = DataBindingUtil.setContentView(this, R.layout.activity_jetpack_lifecycle_databindingdemo)
// 设置 activity 的布局,并获取绑定对象(方式二)
mBinding = ActivityJetpackLifecycleDatabindingdemoBinding.inflate(layoutInflater)
setContentView(mBinding.root)
val myModel = MyDataBindingModel()
// 指定绑定对象的 lifecycleOwner(用于实现 lifecycleOwner 不在前台则不更新数据)
mBinding.lifecycleOwner = this
// 在 xml 的 data 中声明的变量这里都可以使用
mBinding.buttonText = "我是一个按钮" // 这是一次绑定的方式,这种方式不支持单向绑定或双向绑定
mBinding.myModel = myModel
// 在 xml 中的控件,这里也可以通过其 id 使用
mBinding.button1.setOnClickListener {}
// 数据发生变化时的通知
myModel.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
if (propertyId == BR.name) {
Log.d("lifecycle", "BR.name, ${myModel.name}")
}
}
})
lifecycleScope.launch {
repeat(1000) {
delay(1000)
val dateFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.ENGLISH)
val time = dateFormat.format(Date())
// 本例用于演示单向绑定
myModel.name = "${time}"
}
}
// 数据发生变化时的通知
// 本例用于演示双向绑定,即除了数据层变化会通知 UI 更新数据外,UI 中的数据发生了变化也会通知数据层
myModel.country.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
Log.d("lifecycle", "country: ${myModel.country.get()}")
}
})
}
override fun onDestroy() {
super.onDestroy()
mBinding.unbind()
}
}
// 自定义类如果想实现单向绑定或双向绑定,需要继承 BaseObservable 类
class MyDataBindingModel : BaseObservable() {
// 这个 @get:Bindable 注解是必不可少的(如需使用 DataBinding 相关注解,则需要在 gradle 中配置 apply plugin: 'kotlin-kapt')
@get:Bindable
var name: String = "123456789"
set(value){
field = value
// BR 是由 gradle 生成的,这里能用 BR.name 是因为 name 属性添加了 @get:Bindable 注解
// notifyPropertyChanged() - 更新 UI 上的此对象指定字段
// notifyChange() - 更新 UI 上的此对象的所有字段
notifyPropertyChanged(BR.name)
}
// 通过如下几个类可以实现指定类型数据的单向绑定或双向绑定(下面这几个类都继承自 BaseObservable 类)
// ObservableBoolean, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble 等
// ObservableField<T>
// ObservableList, ObservableMap
var country: ObservableField<String> = ObservableField<String>()
// 本例用于演示事件绑定并传参
fun showName(name: String) {
Toast.makeText(MyApplication.getInstance().applicationContext, name, Toast.LENGTH_SHORT).show()
}
companion object {
// 本例用于演示静态方法的绑定
@JvmStatic
fun substr8(name: String): String {
return name.substring(0, 8)
}
// 本例用于演示 @BindingAdapter 注解的用法
// 此方法将接管布局文件中的 app:imageUrl 绑定的数据(注:通过 @{} 绑定的数据才会被接管)
@JvmStatic
@BindingAdapter(value = ["imageUrl"]) // 如需使用 DataBinding 相关注解,则需要在 gradle 中配置 apply plugin: 'kotlin-kapt'
fun loadImage(imageView: ImageView, loadUrl:String) {
Glide.with(imageView).load(loadUrl).into(imageView)
}
}
}
/layout/activity_jetpack_lifecycle_databindingdemo.xml
<?xml version="1.0" encoding="utf-8"?>
<!--如需使用 DataBinding,则布局的根节点必须是 layout-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!--
本布局文件的文件名为 activity_jetpack_lifecycle_databindingdemo.xml
gradle 会为其生成一个名为 com.webabcd.androiddemo.databinding.ActivityJetpackLifecycleDatabindingdemoBinding 的类
-->
<data>
<!--
在 data 中也可以指定生成的类的类名
下面这句 gradle 会为其生成一个名为 com.webabcd.androiddemo.databinding.MyBinding 的类
-->
<!--data class="MyBinding"-->
<!--
在 data 中也可以指定生成的类的类全名
下面这句 gradle 会为其生成一个名为 com.webabcd.CustomBinding 的类
-->
<!--data class="com.webabcd.CustomBinding"-->
<!--声明一个指定名称的变量,并指定其类全名-->
<variable name="myModel" type="com.webabcd.androiddemo.jetpack.lifecycle.MyDataBindingModel" />
<!--先通过类全名导入一个类,然后再声明一个指定名称的变量,并指定其类名-->
<import type="com.webabcd.androiddemo.jetpack.lifecycle.MyDataBindingModel" />
<variable name="myModel2" type="MyDataBindingModel" />
<!--先通过类全名导入一个类并指定其别名,然后再声明一个指定名称的变量,并指定其类别名-->
<import type="com.webabcd.androiddemo.jetpack.lifecycle.MyDataBindingModel" alias="MyAlias" />
<variable name="myModel3" type="MyAlias" />
<!--声明基本类型的变量-->
<variable name="buttonText" type="String" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--
@{} - 单项绑定
@={} - 双向绑定
@{()->...} - 事件绑定并传参
-->
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="@{buttonText}"
android:onClick="@{()->myModel.showName(myModel.name)}"/>
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{myModel.name}"/>
<EditText
android:id="@+id/editText1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={myModel.country}"/>
<!--绑定静态方法-->
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{MyDataBindingModel.substr8(myModel.name)}"/>
<!--绑定支持简单表达式-->
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="输入框中的字符长度小于 1 个我就显示,否则我就隐藏"
android:visibility="@{myModel.country.length() < 1 ? android.view.View.VISIBLE : android.view.View.GONE}"/>
<!--
本例用于演示 @BindingAdapter 注解的用法
app:imageUrl 的绑定数据由后台指定函数接管(注:通过 @{} 绑定的数据才会被接管)
-->
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:imageUrl='@{"https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png"}'/>
</LinearLayout>
</layout>