Search code examples
androidkotlinandroid-imageviewandroid-databinding

Cannot find the getter for attribute ImageView while using Databinding in kotlin


I am trying to learn data binding with Kotlin and I was able to implement it successfully for edit text and text views. After that I am trying to use it for Image Views. I am currently trying to give an option to users to choose their profile picture by clicking on the imageview. This code works properly but when I try to set the image to the view using data binding adapter , I get the following error.

Found data binding errors. ****/ data binding error ****msg:Cannot find the getter for attribute 'android:userImage' with value type java.lang.String on de.hdodenhof.circleimageview.CircleImageView. file:/home/parangat-pt-p10/AndroidStudioProjects/ReUsableAndroid/reusable_android/app/src/main/res/layout/activity_signup.xml loc:25:12 - 31:48 ****\ data binding error ****

Below is my code for the same.

Layout code of ImageView

<de.hdodenhof.circleimageview.CircleImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_gravity="center_horizontal"
            android:userImage="@{signup.userImage}"
            android:id="@+id/iv_user"
            android:src="@drawable/profile"/>

Model class code

    class Signup {
    var userImage=""
    var firstName=""
    var lastName=""
    var phoneNumber=""
    var postCode=""
    var country=""
    var email=""
    var password=""
    var confirmPassword=""
    var isAcceptTerms=false

    @BindingAdapter("android:userImage")
    fun loadImage(view: CircleImageView, imageUrl: String) {
        userImage=imageUrl
        Glide.with(view.context).load(imageUrl).into(view)

    }
}

And this is what I am doing after user selected image

override fun onSingleImageSelected(uri: Uri?) {
    signupBinding.signup?.loadImage(iv_user,uri.toString())

}

Since this is written in kotlin, therefore there is no need to define the getter and setter methods but the error states that no getter method found.

As suggested by Enzokie, I created the binding Adapter in separate file like below

@BindingAdapter("userImage")
fun loadImage(view: CircleImageView, imageUrl: String) {
    Glide.with(view.context).load(imageUrl).into(view)
}

But I still have the same issue.


Solution

  • Correct Approach

    • Use either Observable / Live Data.
    • Make a binding adapter class individually and don't mess-up things in model.
      • Yes tutorials do that, because they are just teaching you.
      • Just make one common binding adapter (like android:src) for whole app.
    • No need to use custom namespace, until when you need it. So you can use android:src instead of android:userImage.
    • No need to use CircleImageView in BindingAdapter, make common adapter with ImageView because CircleImageView is child of ImageView.

    Final code

    • If you need to manually change fields like signup.userImage = "someUrl" then use Bindable and notify, other wise no need of both.
    • If you use ObservableField instead of extending BaseObservable class, then you don't need to use Bindable and notify.

    Signup.class

    class Signup : BaseObservable() {
        @get:Bindable
        var userImage: String = ""
            set(value) {
                field = value
                notifyPropertyChanged(BR.userImage)
            }
    }
    

    DataBindingAdapter.kt

     // binding adapter for setting url/uri on ImageView
    @BindingAdapter("android:src")
    fun setImageUrl(view: ImageView, url: String) {
        Glide.with(view.context).load(url).into(view)
    }
    

    layout.xml

    <de.hdodenhof.circleimageview.CircleImageView
                ...
                android:src="@{signup.userImage}"/>
    

    Now you can set binding.signup.userImage = "Url", it will refract on UI automatically.

    That's all!

    Reason of Fail

    When you use data binding, and you want UI automatic update after setting fields. then your model should be one of below :

    In your case, initially your URL is empty (""). Now when you set image after some time programmatically, then UI is not notified because you are not using any observing option like I said above.

    Bit more info

    The difference between both is, Live data is Android Lifecycle Aware (Activity/ Fragments/ Services).

    LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.