android 数据绑定(6)自定义绑定方法、双向数据绑定

1.官方文档

  https://developer.android.com/topic/libraries/data-binding/binding-adapters

  https://developer.android.com/topic/libraries/data-binding/two-wa

2.双向绑定

2.1 单向的问题

  默认是单向绑定,控件与用户的交互(如输入新的名字,点单选框复选框)不会自动修改对应的绑定数据对象,要在代码里手动处理相应的控件事件,在事件函数里修改数据对象。如:

 1     val data = Data()
 2     lateinit var binding : Way2Binding
 3     val nameWatcher = object : TextWatcher{
 4         override fun afterTextChanged(p0: Editable?) {
 5             val txt = p0.toString()
 6             data.name = txt
 7             binding.invalidateAll()
 8         }
 9         override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
10         override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
11     }
12 
13     fun initBinding(){
14         binding.data = data
15         binding.normalEdt.addTextChangedListener(nameWatcher)
16     }

2.2 使用双向绑定

  使用双向绑定可以简化这个问题,减少相应代码。

  用 @={} 表示可接收属性的数据更改并同时监听用户更新.

1       <EditText
2             ...
3             android:text='@={data.name,default=@string/data_name}'
4             .../>

  这样当用户在这个EditText上输入新的内容后,直接更新到绑定的数据对象。

  注意: @={} 不可以使用格式化的@string/xx   

2.3 自定义的属性使用双向绑定

a.自定义属性的 setXXX 

1     @BindingAdapter("dataName")
2     @JvmStatic fun setDataName(edt : EditText, txt : String){
3         edt.setText(txt)
4     }

b.自定义属性的 getXXX

1     @InverseBindingAdapter(attribute = "dataName")
2     @JvmStatic fun getDataName(edt : EditText) : String{
3         return edt.text.toString()
4     }

注意:attribute =  不可少,否则编译不过。

c.编写属性变化监听器及想要监听的事件

 1     @BindingAdapter("dataNameAttrChanged")
 2     @JvmStatic fun setListener(edit : EditText, listener: InverseBindingListener?) {
 3         Log.e("dataNameAttrChanged","txt = ${edit.text.toString()}")
 4         var txt = ""
 5         edit.addTextChangedListener(object : TextWatcher{
 6             override fun afterTextChanged(p0: Editable?) {
 7                 Log.e("dataNameAttrChanged","afterTextChanged")
 8                 if (txt != p0.toString()){
 9                     listener?.onChange()
10                     txt = p0.toString()
11                 }
12             }
13             override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
14             override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
15         })
16     }
  • InverseBindingListener 是属性变化监听器
  • 这个 EditText 监听的事件是 textChanged

注意:  listener?.onChange() 容易死 循环,要判断下原内容与新内容,不一样才调用这个函数。上述代码中的高亮部分。

d.布局文件中使用

    <EditText  app:dataName='@={data.name}'  ... >

2.4 类型转换的双向绑定

使用类型转换时,也可以使用双向绑定,但是要指定一个反向转换的函数。用 @InverseMethod("转换函数名") 来声明。

 1 @file:JvmName("Converter")
 2 
 3 package com.example.databind
 4 import androidx.databinding.InverseMethod
 5 
 6 
 7 class A (var name: String)
 8 
 9 @InverseMethod("a2String")
10 fun string2a(string: String) : A{
11     return A(string)
12 }
13 
14 fun a2String(a : A) : String{
15     return a.name
16 }

在布局文件中

    <EditText android:text='@={Converter.a2String(a)}' />

2.5 支持双向绑定的内置属性

控件属性绑定适配器所在的类
AdapterView android:selectedItemPosition
android:selection
AdapterViewBindingAdapter
CalendarView android:date CalendarViewBindingAdapter
CompoundButton android:checked CompoundButtonBindingAdapter
DatePicker android:year
android:month
android:day
DatePickerBindingAdapter
NumberPicker android:value NumberPickerBindingAdapter
RadioButton android:checkedButton RadioGroupBindingAdapter
RatingBar android:rating RatingBarBindingAdapter
SeekBar android:progress SeekBarBindingAdapter
TabHost android:currentTab TabHostBindingAdapter
TextView android:text TextViewBindingAdapter
TimePicker android:hour
android:minute
TimePickerBindingAdapte

3.自定义绑定

3.1 系统定义的绑定方法

  假设在绑定布局文件中使用app:aaaaa 的属性,绑定库自动尝试查找方法 setAaaaa(arg)

  • 按app:aaaaa 时传递的参数, 找到参数匹配或兼容的那个方法setAaaaa 
  • 不会考虑属性的命名空间
  • 搜索方法时仅使用属性名称和类型
  • 假设类Student包含 setTtttt 这个方法,那么可以直接在布局文件中使用 app:ttttt 这个属性。

3.2 自定义绑定的方法

  • a.重新绑定属性与对应的方法
    1     @BindingMethods(value = [
    2     BindingMethod(
    3             type = ImageView::class,
    4             attribute = "android:tint",
    5             method = "setImageTintList")])

 上述代码把 android:tint 这个属性绑定到了setImageTintList方法。官方文档中并没有给出这段应该放哪里,放在类里面编译不过,通过官方完整示例中发现它是全局的。

 

  • b.修改系统已经定义好的方法
     1         @androidx.databinding.BindingAdapter("android:paddingLeft")
     2         @JvmStatic
     3         fun setPaddingLeft(view: View, oldPadding: Int, newPadding: Int) {
     4             if (oldPadding != newPadding) {
     5                 view.setPadding(12,
     6                         view.getPaddingTop(),
     7                         view.getPaddingRight(),
     8                         view.getPaddingBottom())
     9             }
    10         }

    重新定义了 android:paddingLeft属性,注意,调用的时候  android:paddingLeft='@{16}' 才匹配这个方法,android:paddingLeft="16dp" 还是系统的那个方法。

   出现冲突时,自定义的绑定方法会替换数据绑定库提供的默认方法.

   重新定义系统已经存在的属性没意义,这个方法要以 '@{16}‘ 这种 方式调用,android:paddingLeft="16dp" 无效

  • c.把多个属性联合在一起,绑定个方法
    1         @JvmStatic
    2         @androidx.databinding.BindingAdapter(value = ["imageUrl", "error"], requireAll = false)
    3         fun loadImage(view: ImageView, url: String, error: Drawable) {
    4             Picasso.get().load(url).error(error).into(view)
    5         }

    requireAll = false 的含义是 单独使用imageUrl,error其中一个属性的时候,就绑定这个方法,true就是必需同时使用这两个属性的时候才绑定这个方法。

    使用

    <ImageView
                android:id="@+id/imageView2"
                android:layout_width="64dp"
                android:layout_height="64dp"
                app:error='@{@drawable/error}'
                app:imageUrl='@{"sdfsd.com/fef.ppppjnpng"}'
                />

3.3 类型转换 @BindingConversion

作用:使用这个 注解可以定义一个类型A到类型B在转换函数,函数名的格式为

convertAToB,如 convertStringToData
1         @JvmStatic
2         @androidx.databinding.BindingConversion
3         fun convertStringToData(name : String) = Data()

 

原文地址:https://www.cnblogs.com/mhbs/p/12059043.html