To use data binding, you must be able to use the binding adapter, which can be combined with the Kotlin code to achieve the effect of the data binding library, which is the code simplicity. If I can define some logic in XML, I can do it in one line of code, not only will it be reusable but the overall simplicity of the code will be greatly improved, and of course this part is also difficult to understand, especially if something goes wrong and it’s not easy to find.

preface

In fact, the above mentioned event handling and we are very common some property values such as TextView text are implemented by this, so it is necessary to understand the principle and use. So the data binding library now provides three ways to set the property values and process the logic.

Automatic selection method

It makes sense that the eigenvalues are set by setXXX in the view code. For example, if a property value is example, the library automatically tries to find a method setExample(arg) that takes a compatible type as a parameter. The search method is based on the property name and type.

Another example of an official document is:

android:text="@{user.name}"

Expression, the library looks for a setText(arg) method that accepts the return type of user.getName(). If the return type of user.getName() does not exist, it cannot be found.

In summary, the automatic selection method finds the corresponding function based on the setter method of the property value.

Specify a custom method name

In this case, you can associate a property value with a method that already exists.

For example, the Android :tint property is associated with setImageTintList, not setTint, so we use @bindingMethods to specify the method name for the property value:

@BindingMethods(value = [
        BindingMethod(
            type = android.widget.ImageView::class,
            attribute = "android:tint",
            method = "setImageTintList")])
Copy the code

In this case, using Tint in the ImageView view, you automatically call the setImageTintList method to set its value.

For example, the data binding library provides the following BindingMethod:

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

Provides custom logic to BindingAdapter

If you want to define a custom property value and a custom method, you need to define more than one. If you want to define a custom property value and a custom method, you need to bind the BindingAdapter to use it.

Let’s start with the official example. Now I want to setPaddingLeft for my view, but there’s no setPaddingLeft method, there’s only setPadding method that sets paddingLeft in four directions, Use the BindingAdapter to bind the paddingLeft property to a custom method:

@BindingAdapter("android:paddingLeft")
    fun setPaddingLeft(view: View, padding: Int) {
        view.setPadding(padding,
                    view.getPaddingTop(),
                    view.getPaddingRight(),
                    view.getPaddingBottom())
    }
Copy the code

For example, if you want to set the url for an image, you can use the url for the image. You can also use the url for the image. If you want to set the url for the image, you can use the url for the image. If we do the ImageView, the code is:

 @BindingAdapter("imageUrl", "error")
    fun loadImage(view: ImageView, url: String, error: Drawable) {
        Picasso.get().load(url).error(error).into(view)
    }
Copy the code

Ok, so that’s the end of the official example, notice the parameters, the first one is the View that you need to set, and then the values.

The other problem is that there are multiple property values, and IN the method I have specified the parameter type of the property value in the method, for example, the URL above is String, the error is Drawable, and the XML conditions are both met, so is there a way to call it with incomplete parameters? Set the requireAll flag to false, which means the requireAll flag is not completely required.

  @BindingAdapter(value = ["imageUrl", "placeholder"], requireAll = false)
    fun setImageUrl(imageView: ImageView, url: String?, placeHolder: Drawable?) {
        if (url == null) {
            imageView.setImageDrawable(placeholder);
        } else {
            MyImageLoader.loadInto(imageView, url, placeholder);
        }
    }
Copy the code

extension

Now that I’ve covered the simplest and most commonly used ones, I can use some extensions and optimizations in this section. First of all, Kotlin language extension function is really very nice, can greatly improve the code simplicity, for example, I am all View can set the property value, that is to expand the View, the EditText property value, that is to expand the EditText, which is convenient and easy to find, such as an example in this project:

@BindingAdapter(value = ["afterTextChanged"]) fun EditText.afterTextChanged(action: () -> Unit){ this.addTextChangedListener(object : TextWatcher{ override fun beforeTextChanged(s: CharSequence? , start: Int, count: Int, after: Int) { } override fun onTextChanged(s: CharSequence? , start: Int, before: Int, count: Int) { } override fun afterTextChanged(s: Editable?) { action() } }) }Copy the code

This code can be written directly in an EditTextKt file.

Another extension is the use of higher order functions, which are not very familiar in use, but need to be improved later. For example, I need to check if the login box is empty or regular after the EditText content has changed. In this case, I add a callback to afterTextChanged. So you can define a higher-order function for an action, just like in the example code above.

conclusion

In order for the code to be concise and readable, data binding must be used, and using binding adapters for complex logic can simplify the code.