BaseObservable DataBinding Progression 3 BindingAdapter and BindingConversion DataBinding progression 4 Two-way data binding

4. BindingAdapter

The BindingAdapter is used as a framework for setting a value. There are generally three ways to set a value.

  • Automatic selection method
  • Use @bindingMethods (specify custom method names)
  • @bindingAdapter (provides custom logic)

4.1 Automatic selection method

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{user.name}" />
Copy the code

For example, android:text=”@{user.name}”. The library automatically looks for setText methods whose parameters are of type user.name. For example, if user.name is returned as String, the library will automatically look for setText(String) methods to call. So that’s how you automatically look it up.

4.2 @ BindingMethods usage

@bindingMethods is an annotation provided by the DataBinding library to map a property in a View to its corresponding setter method name, For example, android:textColorHint is the same method as setHintTextColor. In this case, the name of the property is inconsistent with the name of the corresponding setter method. This requires binding the property to the corresponding setter method using the @bindingMethods annotation so that DataBinding can find the corresponding setter method based on the property value. Example from TextView source:

@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 {
    / /...
}
Copy the code

CustomTextView: Android :custom_text calls the showCustomToast method

@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()
    }
}
Copy the code

The XML layout code is as follows:

<? 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>
Copy the code

Android :custom_text=”@{teach.name}” We can see that the Android :custom_text layout is called in this form. The showCustomToast method in the CustomTextView class is called. That’s what the BindingMethod does. Other complete code

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
}
Copy the code

4.3 Usage of BindingAdapter

BindingAdapter generally has two scenarios. One is to perform some custom logical processing on the existing attributes. One is completely custom properties, custom logic.

4.3.1 Custom logic for Existing Attributes

The BindingAdapter can be used when some attributes require custom logic. Examples are as follows:

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"/>
Copy the code
  • @bindingAdapter (” Android :text”) We redefined the logic of Android :text. When using DataBinding’s setText, val STR = text.tostring ().tolowercase () changes the content toLowerCase, Textview.text = STR +”= CCMEND =” and add the string “= CCMEND =” to the end.
  • The first TextView uses android:text=”@{teacher.name}”, which is a DataBinding form, so when we pass in teacher.name as CTest, we will finally input CTest = ccMEND =
  • The two TextViews do not use DataBinding, so CTest is output.

This is the first use of the @BindingAdapter, which allows you to customize the logic for certain attributes

4.3.2 Customizing Attributes

We can customize the attributes to use the following code:

object ImageViewAdapter {
    @JvmStatic
    @BindingAdapter("imageurl")
    fun loadImage(imageView: ImageView, imageUrl: String) {
        Glide.with(imageView.context).load(imageUrl).into(imageView)
    }
}
Copy the code

XML layout

<? 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>
Copy the code

The activity code:

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"}}}Copy the code

You can also use adapters that accept multiple properties

@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) }// Use of 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"/>
Copy the code

If you want to make multiple properties optional, you simply add a requireAll variable.

@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) }// In this XML layout, use one parameter, but both parameters are acceptable
<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"/>
Copy the code

RequireAll = false. Indicates that the custom properties are selected so that when the layout is used, you do not need to write all the properties.

Five: BindingConversion

Data, or types, can be converted using BindingConversion. Examples of data conversion:

@BindingConversion
@JvmStatic
fun setText(text: String):String{
    return "$text=Add=ConversionEnd="
}
Copy the code

The XML file

<TextView
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:text='@{"CTest"}'
   android:textAllCaps="false"/>
Copy the code

Add= Add=ConversionEnd= string after using the suffix to the string. The final output will be “CTest=Add=ConversionEnd=” type conversion example:

@BindingConversion
@JvmStatic
fun conversionToDrawable(text: String):Drawable{
  return when(text){
      "Red"->ColorDrawable(Color.RED)
       else->ColorDrawable(Color.WHITE)
  }
}
Copy the code

XML key code:

 <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="@ {` red `}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
Copy the code

Android :background=”@{red}” {BindingConversion = String = Drawable; The TextView background is red when you run it. Indicates that the conversion is successful. Note: If BindingConversion and BindingAdapter are used. Both take effect, but the BindingConversion takes precedence over the BindingAdapter.