DataBinding 基础篇一
DataBinding 进阶篇二 BaseObservable
DataBinding 进阶篇三 BindingAdapter以及BindingConversion
DataBinding 进阶篇四 双向数据绑定
四:BindingAdapter(绑定适配器)
BindingAdapter是作为设置某个值的框架来使用。一般有三种方法去设置值。
- 自动选择方法
- 使用@BindingMethods(指定自定义方法名称)
- @BindingAdapter(提供自定义的逻辑)
4.1 自动选择方法
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}" />
比如android:text="@{user.name}"为例,库会去自动的查找setText方法,并且setText方法的参数,是user.name的类型的参数。比如我们这里user.name是返String,那么库就会自动去查找setText(String)的方法去调用。这个就是自动查找的方式。
4.2 @BindingMethods用法
@BindingMethods是 DataBinding 库提供的一个注解,用于当 View 中的某个属性与其对应的 setter 方法名称不对应时进行映射,如 TextView 的属性 android:textColorHint 与之作用相同的方法是 setHintTextColor 方法,此时属性名称与对应的 setter 方法名称不一致,这就需要使用 @BindingMethods 注解将该属性与对应的 setter 方法绑定,这样 DataBinding 就能够按照属性值找到对应的 setter 方法了。TextView源码中的例子:
@BindingMethods({
@BindingMethod(type = TextView.class, attribute = "android:autoLink", method = "setAutoLinkMask"),
@BindingMethod(type = TextView.class, attribute = "android:drawablePadding", method = "setCompoundDrawablePadding"),
@BindingMethod(type = TextView.class, attribute = "android:editorExtras", method = "setInputExtras"),
@BindingMethod(type = TextView.class, attribute = "android:inputType", method = "setRawInputType"),
@BindingMethod(type = TextView.class, attribute = "android:scrollHorizontally", method = "setHorizontallyScrolling"),
@BindingMethod(type = TextView.class, attribute = "android:textAllCaps", method = "setAllCaps"),
@BindingMethod(type = TextView.class, attribute = "android:textColorHighlight", method = "setHighlightColor"),
@BindingMethod(type = TextView.class, attribute = "android:textColorHint", method = "setHintTextColor"),
@BindingMethod(type = TextView.class, attribute = "android:textColorLink", method = "setLinkTextColor"),
@BindingMethod(type = TextView.class, attribute = "android:onEditorAction", method = "setOnEditorActionListener"),
})
public class TextViewBindingAdapter {
//...
}
自己写的例子:
自定义一个CustomTextView,声明android:custom_text会调用到showCustomToast方法,代码如下
@BindingMethods(value = [
BindingMethod(type = androidx.appcompat.widget.AppCompatTextView::class,attribute = "android:custom_text",method = "showCustomToast")
])
class CustomTextView :AppCompatTextView{
constructor(context: Context):this(context,null,0)
constructor(context: Context, attrs: AttributeSet?):this(context,attrs,0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr:Int=0):super(context,attrs,0)
fun showCustomToast(text:String){
Toast.makeText(context,text,Toast.LENGTH_SHORT).show()
}
}
xml布局代码如下:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.example.jepcaktestapp.databinding.Teacher"/>
<variable
name="teacher"
type="Teacher" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.jepcaktestapp.databinding.CustomTextView
android:id="@+id/customTextView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="CustomTextView"
android:custom_text="@{teacher.name}"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
android:custom_text="@{teacher.name}" 我们可以看到android:custom_text布局通过这种形式调用。会调用到CustomTextView类里的showCustomToast方法。这就是BindingMethod的作用。
其他完整代码
class DataBindingTestActivity :AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityDatabindingtestBinding>(this,R.layout.activity_databindingtest)
binding.teacher = Teacher().apply{
name = "ccm"
age = 1
}
}
}
class Teacher {
var name =""
var age = 0
}
4.3 BindingAdapter的用法
BindingAdapter一般有两种场景,一种是对已有的属性需要做一些自定义的逻辑处理。一种是完全自定义的属性,自定义的逻辑。
4.3.1 已有属性自定义逻辑
当某些属性需要自定义逻辑的时候,可以使用BindingAdapter。例子如下:
object TextViewAdapter {
@JvmStatic
@BindingAdapter("android:text")
fun setText(textView: TextView, text: CharSequence) {
val str = text.toString().toLowerCase()
textView.text = str+"=ccmend="
}
}
<TextView
android:id="@+id/tv_teacher_name"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="@{teacher.name}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_teacher_name2"
app:layout_constraintTop_toBottomOf="@+id/tv_teacher_name"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="CTest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
- @BindingAdapter("android:text") 我们重新定义了android:text的逻辑。在使用了DataBinding的setText的时候 val str = text.toString().toLowerCase() 把内容变成小写,textView.text = str+"=ccmend="并且末尾添加上“=ccmend=”字符串。
- 第一个TextView使用了android:text="@{teacher.name}",这种DataBinding的形式,所以当我们传入的teacher.name是CTest的时候,那么最终输入的会是ctest=ccmend=
- 而二个TextView并没有使用DataBinding的,所以输出的是CTest。 这个就是 @BindingAdapter的第一个作用,可以为某些属性自定义逻辑
4.3.2 自定义属性
我们可以自定义属性,去使用如下代码:
object ImageViewAdapter {
@JvmStatic
@BindingAdapter("imageurl")
fun loadImage(imageView: ImageView, imageUrl: String) {
Glide.with(imageView.context).load(imageUrl).into(imageView)
}
}
xml布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.example.jetpackdatabindingtestapp.ui.model.Driver"/>
<variable
name="driver"
type="Driver" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="wrap_content"
app:imageurl="@{driver.url}"
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
activity代码:
class BindingAdapterActivity :AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityBindingadapterTestBinding>(this,R.layout.activity_bindingadapter_test)
binding.driver = Driver().apply{
name = "ccm"
age = 1
url = "https://www.baidu.com/img/bd_logo1.png"
}
}
}
您还可以使用接收多个属性的适配器
@JvmStatic
@BindingAdapter("imageurl","errorD","placeholderD")
fun loadImage(imageView: ImageView, imageUrl: String,errorDrawable:Drawable,placeholderDrawble:Drawable) {
Glide.with(imageView.context).load(imageUrl).apply(RequestOptions().error(errorDrawable).placeholder(placeholderDrawble)).into(imageView)
}
// xml的使用
<ImageView
android:id="@+id/iv_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/customTextView"
android:layout_width="wrap_content"
app:imageurl="@{driver.url}"
app:errorD="@{@drawable/ic_launcher_background}"
app:placeholderD="@{@drawable/ic_launcher_background}"
android:layout_height="wrap_content"/>
上面的写法,有三个属性,那么布局中必须得要同时使用三个属性,如果想要使的多个属性变成可选,那么只需要在添加一个requireAll变量即可。
@JvmStatic
@BindingAdapter(value=["imageurl","errorD","placeholderD"],requireAll = false)
fun loadImage(imageView: ImageView, imageUrl: String,errorDrawable:Drawable,placeholderDrawble:Drawable) {
Glide.with(imageView.context).load(imageUrl).apply(RequestOptions().error(errorDrawable).placeholder(placeholderDrawble)).into(imageView)
}
// 这样xml布局里,使用一个参数,两个参数都可以
<ImageView
android:id="@+id/iv_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/customTextView"
android:layout_width="wrap_content"
app:imageurl="@{driver.url}"
app:errorD="@{@drawable/ic_launcher_background}"
android:layout_height="wrap_content"/>
requireAll = false。表示把自定义属性设置可可选,这样布局使用的时候,就无需把所有属性都写上了。
五:BindingConversion
使用BindingConversion可以对数据,或者类型进行转换.
数据转换的例子:
@BindingConversion
@JvmStatic
fun setText(text: String):String{
return "$text=Add=ConversionEnd="
}
xml文件
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text='@{"CTest"}'
android:textAllCaps="false"/>
在使用到字符串的后缀添加=Add=ConversionEnd=字符串。最终输出的会是"CTest=Add=ConversionEnd="
类型转换的例子:
@BindingConversion
@JvmStatic
fun conversionToDrawable(text: String):Drawable{
return when(text){
"红色"->ColorDrawable(Color.RED)
else->ColorDrawable(Color.WHITE)
}
}
xml关键代码:
<TextView
android:id="@+id/tv_teacher_name"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="CTest"
android:background="@{`红色`}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:background="@{红色}"本来这种代码肯定是会报错的,因为background不能接收String类型,使用了BindingConversion可以将String类型转换为Drawable。运行会发现TextView背景是红色的。说明是转换成功的。
注意:如果使用了BindingConversion跟BindingAdapter。两者都会生效,但BindingConversion的优先级会高于BindingAdapter。