DataBinding 进阶篇三 BindingAdapter以及BindingConversion

·  阅读 1162

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。

分类:
Android
分类:
Android