DataBinding 数据绑定。是Google对MVVM在Android上的一种实现。能够直接绑定数据到xml中。
dataBinding:官方文档 (需要梯子)
我的开发环境:jdk8,AndroidStudio3.4(中文社区),gradle插件5.1.1,Kotlin插件1.3.40
Android Studio 2.0以前databinding需要通过添加依赖库com.android.databinding,之后被内置。
之前的初始配置(在module的build.gradle中):
apply plugin: 'kotlin-kapt'
android {
...
dataBinding {
enabled true
}
}
dependencies{
kapt "com.android.databinding:compiler:3.1.2"
}
所以之后的环境配置(在module的build.gradle中):
android {
...
//开启dataBinding
dataBinding {
enabled true
}
}
开始使用
- 数据类
data class DataBean(
var name: String,
var age: Int,
var isShow: Boolean
)
- 创建布局
需要在原来的布局外面嵌套一层<layout>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<!--数据绑定-->
<data>
<!--name值自定义,type要绑定的数据类-->
<variable name="user" type="com.zxx.zmvvm.service.data.DataBean" />
<!--也可以不必用数据类-->
<variable name="otherData" type="String" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:text="@{user.name}" />
<TextView
android:id="@+id/tv_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:text="@{String.valueOf(user.age)}" />
</LinearLayout>
</layout>
需要注意:
- xml最外层需要嵌套一层<layout>
- 如果想要绑定数据在通过<data>的<variable>属性绑定,绑定后还可以通过variable的name属性使用数据
- 绑定Variable
PS:在绑定数据之前需要make以下工程,让其生成Binding类
生成的databinding.png
生成规则:
- 默认生成规则:xml通过文件名称生成,使用下划线切割大写和小写。比如我的activity_main.xml就生成ActivityMainBinding
- view的生成规则相似,如TextView的id 是tv_name,则生成tvName
- 如果想自定义生成的class名:
<data class=“DefalutClass”>
…
</data>
全部Binding实例的生成都能够通过DataBindingUtil进行,方法名与该view的原inflate方法一致。
所以在Activity中绑定Variable:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//这个不用了,要通过下面的方法来绑定视图
//setContentView(R.layout.activity_main)
//布局编辑好后需要make Project
//布局绑定从setContentView( laytouId )换成了 DataBindingUtil.setContentView( activity, layoutId)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
//直接给数据赋值(此处的user就是布局里variable的name属性)
binding.user = DataBean("Zxx", 18, false)
//----------------------------------------------
//或者通过setVariable
val bean = DataBean("Zxx", 18, false)
//BR是make生成的类
binding.setVariable(BR.user, bean)
//-----------------------------------------------
//或者绑定视图 测试没有显示出来
binding.tvName.text = "ZZZ"
binding.tvAge.setText("20")
//但是打印正确
Log.e("-Tag-", binding.tvName.text as String)
//----------------------------------------------
//最初不用databinding,是通过Kotlin插件导入id来实现的
tv_name.text = "Zxx" // 使用了kotlin-android-extensions
}
}
在Fragment中绑定variable:
class UserFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
//把原来的inflater.inflate换成了DataBindingUtil.inflate
val binding = DataBindingUtil.inflate<FragmentUserBinding>(
inflater, R.layout.fragment_user, container, false)
//或者
// val view = inflater.inflate(R.layout.fragment_user, container, false)
// val binding = DataBindingUtil.bind<FragmentUserBindingImpl>(view)
binding?.viewModel = UserViewModel(context!!)
return binding?.root
}
}
事件绑定
· 方法引用:
直接在xml布局中引入android:onClick,android:onLongClick,android:onTextChanged;注意:方法签名需和相应listener方法一致 且方法里面需要参数view。
调用方式为variable.method或者variable::method
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<!--在这里定义方法通过activity引用,其实任何类都行-->
<variable name="activity" type="com.zxx.zmvvm.MainActivity" />
<!--通过presenter引用方法-->
<variable name="presenter" type="com.zxx.zmvvm.presenter.MainPresenter" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--调用方式为variable.method或者variable::method-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{activity::onClick}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{activity.onClickTwo}"/>
<!--方法名随意-->
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="输入信息"
android:onTextChanged="@{presenter::onTextChanged}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{presenter.onClickTwo}"/>
</LinearLayout>
</layout>
代码中:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
...
//为了绑定方法
binding.activity = this
binding.presenter = MainPresenter()
}
//注意:方法里面需要参数view
fun onClick(view: View) {
Toast.makeText(this, "hello", Toast.LENGTH_SHORT).show()
}
fun onClickTwo(view: View) {
Toast.makeText(this, "world", Toast.LENGTH_SHORT).show()
}
}
class MainPresenter {
fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
Log.e("-Tag-", s.toString())
}
fun onClickTwo(view: View) {
Log.e("-Tag-", "hello world")
}
}
· 监听绑定(lambda):
监听绑定可以使用复杂的参数。
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isShow ? View.GONE : View.VISIBLE}"
android:onClick="@{()->presenter.onParameterListener(user)}"/>
class MainPresenter {
fun onParameterListener(user: DataBean) {
Log.e("-Tag-", user.name)
}
}
两者的区别主要是方法引用会在绑定数据时创建好View的onClickListener,而绑定监听在事件发生时才会创建。
DataBinding表达式:
与我们平常用的表达式基本相同,只是这是在xml里用
- 算术表达式 + - / * %
- 字符串合并 +
- 逻辑运算 && ||
- 二元表达式 & | ^
- 一元表达式 + - ! ~
- 二进制移位 >> >>> <<
- 比较 == > < >= <=
- instanceof
- Grouping ()
- 文字类型 - character, String, numeric, null
- 强转 Cast
- 方法调用
- 属性引用
- 数组 [] (须要注意的是数组的越界)
- 三元 ?:
- 空合并 ?? (如 a??b 意思是如果a不为空选a,如果为空选b)
尚且不支持this, super, new, 以及显示的泛型调用。
绑定更新:
数据类更新后并不会更新UI,而数据绑定后,我们当然会希望数据变更UI也会即时刷新。这时就要用到观察者Observable。
1. BaseObservable
主要针对数据类的,把我们写的实体类直接继承BaseObservable.
class UserBean : BaseObservable() {
@get:Bindable
var firstName: String? = null
set(firstName) {
field = firstName
// 每当值set()后,通过notifyPropertyChanged()方法去指定更新
// 可更新某个值,可以更新整个数据,取决于你BR后面的属性
// BR._all 可更新所有的BR中字段相关联的UI
//这里只更新firstName属性
notifyPropertyChanged(BR.firstName)
}
@get:Bindable
var lastName: String? = null
set(lastName) {
field = lastName
notifyPropertyChanged(BR.lastName)
}
}
用 @Bindable 标记过 getter 方法会在 BR 中生成一个 entry,为了更新我们还是需要通过notify发出通知。BaseObservable提供了notifyChange()和notifyPropertyChanged(int)方法,前者会刷新全部的值域,后者则仅仅更新相应BR的flag。所以通过调用notifyPropertyChanged(BR.firstName)来通知系统 BR.firstName 这个 entry 的数据已经发生变化,需要更新 UI。
2. Observable Fields:
这个主要是观察具体字段值,提供的方法有:ObservableBoolean,ObservableByte,ObservableChar,ObservableDouble,ObservableField<T>,ObservableFloat,ObservableInt,ObservableLong,ObservableShort,ViewDataBinding,ObservableParcelable<T extends Parcelable>
class User(){
var firstName: ObservableField<String> = ObservableField()
val lastName = ObservableField<String>()
val age = ObservableInt()
}
<!--更新绑定-->
<variable name="userField" type="com.zxx.zmvvm.service.data.User" />
//使用
binding.userField?.firstName?.set("zzz")
var name = binding.userField?.firstName?.get()
或者直接在布局里声明
<!--更新绑定,其中<是小于号-->
<import type="androidx.databinding.ObservableField" />
<variable name="title" type="ObservableField<String>" />
private val title = ObservableField("defaultTitle")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//布局编辑好后需要make Project
//布局绑定从setContentView( laytouId )换成了 DataBindingUtil.setContentView( activity, layoutId)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.title = title
}
//注意:方法里面需要参数view
fun onClick(view: View) {
title.set("更新绑定")
}
通过该字段的 set(value) /get() 方法来更新值/获取值。
-
Observable Collections:
如果想动态的保存数据,这时候我们会希望使用Map来存储数据结构。Observable提供了ObservableArrayMap()
var userMap: ObservableArrayMap<String, Any> = ObservableArrayMap()
userMap["name"] = "Zxx"
userMap.put("age", 12)
userMap.put("isShow", true)
<!--集合-->
<import type="androidx.databinding.ObservableMap" />
<variable name="user" type="ObservableMap<String,Object>" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{userMap["name"]}'/>
也提供ObservableArrayList()
var userList = ObservableArrayList<Any>()
userList.add("Zxx")
userList.add(18)
userList.add(true)
//layout中直接通过数字下标进行訪问。
参考:
https://www.cnblogs.com/zhchoutai/p/8431033.html
MVVM与DataBinding