I stumbled on Android DataBinding + Kotlin (more BindingAdapter)

Introduction

Using DataBinding, I will write about the stumbling point and how to solve it. (Half is a memorandum) There are a lot of tips when creating a custom setter (BindingAdapter annotation).

BindingAdapter error in Kotlin

When I made a custom setter in Java, I could write it like this.

@BindingAdapter("imageUrl")
public static void loadImage(ImageView view, String url) {
   // do something
}

First, I wrote a custom setter in Kotlin:

class ImageViewBindingAdapter {
    companion object {
        @BindingAdapter("imageUrl")
        @JvmStatic
        fun loadImage(view: ImageView, imageUrl: String) {
            // do something
        }
    }
}

DataBindingAttribute.Companion is not static and requires an object to use, retrieved from the DataBindingComponent.

The static method using companion object will result in the above error. You can write a custom setter by the following method.

object ImageViewBindingAdapter {
    @BindingAdapter("imageUrl")
    @JvmStatic
    fun loadImage(view: ImageView, imageUrl: String) {
        // do something
    }
}

You can also use Kotlin extension functions.

object ImageViewBindingAdapter {
    @BindingAdapter("imageUrl")
    @JvmStatic
    fun ImageView.loadImage(imageUrl: String) {
        // do something
    }
}

XML is the same for both Java and Kotlin.

<ImageView 
    app:imageUrl="@{viewModel.imageUrl}" />

Specify multiple parameters for custom setters in Kotlin

With Java, I could write like this.

@BindingAdapter({"imageUrl", "error"})
public static void loadImage(ImageView view, String url, Drawable error) {
   // do something
}

This is the case with Kotlin.

object ImageViewBindingAdapter {
    @BindingAdapter("imageUrl", "error")
    @JvmStatic
    fun ImageView.loadImage(imageUrl: String, error: Drawable) {
        // do something
    }
}

In XML, write:

<ImageView 
    app:imageUrl="@{viewModel.imageUrl}"
    app:error="@{@drawable/errorImage}" />

As shown below, it does not work well if only some parameters are specified.

<ImageView 
    app:imageUrl="@{viewModel.imageUrl}" />

This is because the BindingAdapter's requireAll attribute defaults to true. In this case, it will not be called unless all parameters are specified. To make it called by some parameters, rewrite BindingAdapter as follows.

@BindingAdapter("imageUrl", "error", requireAll = false)

I want to pass Int, Boolean to BindingAdapter

I wanted to set Int to BindingAdapter, so I wrote the following, but it didn't work.

object ImageViewBindingAdapter {
    @BindingAdapter("imageUrl", "position")
    @JvmStatic
    fun ImageView.loadImage(imageUrl: String, position: Int) {
        // do something
    }
}
<ImageView 
    app:imageUrl="@{viewModel.imageUrl}"
    app:position="1" />

The cause lies in the way it is written in XML, and it works if you change it as follows.

<ImageView 
    app:imageUrl="@{viewModel.imageUrl}"
    app:position="@{1}" />

If you want to pass a Boolean, write:

<ImageView 
    app:imageUrl="@{viewModel.imageUrl}"
    app:dispflag="@{true}" />

I don't know why I have to write this, so please let me know if anyone knows.

I want to pass the resource ID to BindingAdapter

You can import the R class as follows.

<data>
    <import type="com.sample.R"/>
    <variable 
        name="viewModel"
        type="com.sample.TestViewModel" />
</data>
<ImageView 
    app:src="@{viewModel.resourceId ?: R.drawable.error}" />

I want to handle events other than android: onClick

First, I checked if there was an Adapter provided as standard. https://android.googlesource.com/platform/frameworks/data-binding/+/master/extensions/baseAdapters/src/main/java/android/databinding/adapters

For example, if you want to use ʻandroid: onTextChanged of ʻEditTextView, [TextViewBindingAdapter.java](https://android.googlesource.com/platform/frameworks/data-binding/+/master/extensions/baseAdapters/src/ main / java / android / databinding / adapters / TextViewBindingAdapter.java) already has a BindingAdapter so you can use it.

There is also a description of BindingMethod here, so you may not need to bother to prepare a method BindingMethod like set ** Listener.

I want to use Context as an argument

When I want to use a method that has Context as an argument in DataBinding, I referred to the following article. Just pass it as context and it will work.

Handle method that passes context as argument in DataBinding

Recommended Posts

I stumbled on Android DataBinding + Kotlin (more BindingAdapter)
I made a calculator app on Android
I tried using "nifty cloud mobile backend" and "Firebase" Authentication on Kotlin + Android
I stumbled on the Java version in Android Studio, so I will summarize it
[Android Studio] I want to use Maven library on Android
I made a library for displaying tutorials on Android.
What I stumbled upon when installing Ruby on Ubuntu
I want to simplify the log output on Android