0

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.

Enzokie
  • 7,365
  • 6
  • 33
  • 39
Vivek Mishra
  • 5,669
  • 9
  • 46
  • 84

4 Answers4

2

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.

Community
  • 1
  • 1
Khemraj Sharma
  • 57,232
  • 27
  • 203
  • 212
  • I will try this now but issue in my case was that I was not even able to build my project. – Vivek Mishra Oct 11 '18 at 07:19
  • See this answer, I tried to cover up all issues I faced in binding here. https://stackoverflow.com/a/51579759/6891563 – Khemraj Sharma Oct 11 '18 at 07:22
  • How to call the BindingAdapter loadImage method. I can't see in your code where it is called – Vivek Mishra Oct 11 '18 at 07:34
  • Bro, it is data binding, you need not to call it. – Khemraj Sharma Oct 11 '18 at 07:49
  • I added layout code here. You need not set src and userimage both, as I have created binding adapter by name (android:src) so just use it. – Khemraj Sharma Oct 11 '18 at 07:51
  • When url is changed, then binding internally calls binding adapter method. You can see binding internal classes, when project build successful then press alt + F7 on setImageUrl method. – Khemraj Sharma Oct 11 '18 at 07:52
  • If I use @bindable attribute in the model class, it keeps giving error in the BR file. While if I remove it then it build successfully – Vivek Mishra Oct 11 '18 at 08:49
  • BR file mean Binding Resources, It is generated when you have successfully build project. If you are not getting suggestion then import by `import com.yourpackage.BR` – Khemraj Sharma Oct 11 '18 at 09:08
  • I recently read that Bindable should be used when you only want a single field to be observed for data change. But in my case, I have already extended Base observable so I think specifying @Bindable again is not going to work – Vivek Mishra Oct 11 '18 at 09:10
1

try by removing android TAG

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

}

AND

<de.hdodenhof.circleimageview.CircleImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center_horizontal"
        app:userImage="@{signup.userImage}"
        android:id="@+id/iv_user"
        android:src="@drawable/profile"/>
Sayem
  • 4,891
  • 3
  • 28
  • 43
  • I can't add the curly braces inside the Binding adapter annotation . Android Studio gives error as soon as type that – Vivek Mishra Oct 11 '18 at 04:58
1

Use this

@BindingAdapter({"bind:userImage"})

Instead of this

@BindingAdapter("android:userImage")

And in CircleImageView

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

here is the good article for Loading images with data binding

AskNilesh
  • 67,701
  • 16
  • 123
  • 163
  • @VivekMishra check this article https://android.jlelse.eu/loading-images-with-data-binding-and-picasso-555dad683fdc – AskNilesh Oct 11 '18 at 04:55
  • I have some issue with the link's code. Android studio doesn't let me add curly braces inside the binding adapter (). I tried like this `@BindingAdapter({"bind:userImage"})` – Vivek Mishra Oct 11 '18 at 04:57
  • @VivekMishra check this hope this gives u some tips [Custom attributes using BindingAdapters in Kotlin](https://proandroiddev.com/custom-attributes-using-bindingadapters-in-kotlin-971ef8fcc259) – AskNilesh Oct 11 '18 at 05:22
0

Alongwith the answer given by Khemraj make sure you have marked Binding Adapter as @JvmStatic and it should be added above @BindingAdapter annotation. I wasted a whole day as I had added jvmstatic after Binding Adapter annotation.

object ImageBindingAdapter {
@JvmStatic
@BindingAdapter("android:src")
fun setImage(imageView: ImageView, uri: String) {
    Glide.with(imageView.context).load(uri).placeholder(R.drawable.no_image).error(R.drawable.no_image).centerCrop().into(imageView)
}}
Vivek Mishra
  • 5,669
  • 9
  • 46
  • 84