19

I am trying to load image with databinding. But I never got over it. Where's my problem? Below is my code and layout construction.

MyItemViewModel.kt

  @BindingAdapter("imageUrl")
    fun loadImage(view: RoundedImageView, url: String) = Glide.with(view.context).load(url).into(view)

layout.xml

<data>

    <variable
            name="viewModel"
            type="com.myapp.app.ui.activity.albumlist.AlbumItemViewModel"/>
</data>

  <com.makeramen.roundedimageview.RoundedImageView
                android:layout_width="60dp"
                android:id="@+id/ivRoundedAlbum"
                android:layout_marginStart="@dimen/unit_20_dp"
                app:riv_corner_radius="8dp"
                app:imageUrl="@{viewModel.data.cover}"
                android:layout_height="60dp"/>
Hasan Kucuk
  • 2,433
  • 6
  • 19
  • 41
  • 2
    You'll need to make your **fun loadImage** static. Put `@JvmStatic` to it. If it provides error than move it to companion object. – Jeel Vankhede Jul 04 '19 at 14:25
  • 1
    @Hasan What is the exact error is url is "null" or some compile time error is occuring.? – yash786 Jul 04 '19 at 14:25
  • 1
    @JeelVankhede it gives error. I transferred into the model again gives error. ( Only members in named objects and companion objects can be annotated with @JvmStatic) – Hasan Kucuk Jul 04 '19 at 14:31
  • 1
    your function should be void remove = and but {} @HasanKucuk – Ramzy Hassan Jul 04 '19 at 14:34
  • 1
    @yash786 url comes full but I can't load imageview with databinding. – Hasan Kucuk Jul 04 '19 at 14:34
  • 1
    do you have the internet permission in your manifest? – Blackbelt Jul 04 '19 at 14:36
  • 1
    @Blackbelt yes, added. – Hasan Kucuk Jul 04 '19 at 14:37
  • 1
    Can you pls check out the link which I have mentioned in my answer. It will definitely help you – GOVIND DIXIT Jul 04 '19 at 14:38
  • Does this answer your question? [Set drawable resource ID in android:src for ImageView using data binding in Android](https://stackoverflow.com/questions/35809290/set-drawable-resource-id-in-androidsrc-for-imageview-using-data-binding-in-andr) – k4dima Jul 29 '20 at 09:37
  • You can go ahead on the route of creating a BindingAdapter. That is by far the best way I think of loading images in recyclerview items with Glide. [Here's a simple tutorial](https://ayusch.com/databinding-with-glide-android/) if you're into articles. It's the cleanest way to implement this. – Ayusch Jain Aug 10 '20 at 17:17

8 Answers8

13

You need to make url parameter nullable, and guard against null like this:

@BindingAdapter("imageUrl")
fun loadImage(view: RoundedImageView, url: String?) {
    if (!url.isNullOrEmpty()) {
        .....
    }
}
Maddy
  • 4,525
  • 4
  • 33
  • 53
IgorGanapolsky
  • 26,189
  • 23
  • 116
  • 147
7

BindingAdapter methods should be static, so marking it @JvmStatic would help in this case.

But that will generate 'compile time error' that "methods can't be static inside class" and so it should be moved to companion object or named objects.

In your case, you're having method in class member level so moving it to companion object will help. So for MyItemViewModel.kt make companion object and move method there like below :

class MyItemViewModel{
    //Some code
    companion object {

        @JvmStatic
        @BindingAdapter("imageUrl")
        fun loadImage(view: RoundedImageView, url: String) { // This methods should not have any return type, = declaration would make it return that object declaration.
            Glide.with(view.context).load(url).into(view)
        }
    }
    //Some other code
}

Note: Also remove method declaration with =. Binding methods should have return type Unit.


Edit: One can also use method Glide.with(view) as @hmac suggested in comment, but ...

Things to consider before using this Glide.with(view):

  1. Your view should be attached before using it from Activity/Fragment. Best usecase for this method is Custom View/ViewGroup.

  2. Consider layout hierarchy before using this method as too many nested/large hierarchy layouts are discouraged to use that method. It becomes inefficient for such layouts.

  3. Also note that, if view is inside non-support fragment class or context is of non-support fragment than that can produce noisy log as documentation indicates, first migrate to support library (Now considered as AndroidX) instead before using this method!

Jeel Vankhede
  • 11,592
  • 2
  • 28
  • 58
  • Where is the documentation in Glide that dictates BindingAdapter methods should be static? – IgorGanapolsky Nov 09 '19 at 19:00
  • I think using `Glide.with(view)` is preferable, if you check the code and documentation this will respect the lifecycle of the Fragment (or Activity) that the view is shown in. `with(context)` "will only have application level options applied and will not be started or stopped based on lifecycle events...is appropriate for resources that will be used outside of the normal fragment or activity lifecycle" – hmac Apr 03 '20 at 19:30
4

This work fine for me

             <ImageView
                    android:layout_width="0dp"
                    android:layout_height="100dp"
                    android:layout_margin="10dp"
                    android:layout_gravity="center"
                    bind:image="@{subcategory.image}"
                    bind:placeholder="@{@drawable/no_imge}"
                    android:layout_weight="1" />




  @BindingAdapter("image","placeholder")
    fun setImage(image: ImageView, url: String?, placeHolder: Drawable) {

        if (!imageUrl.isNullOrEmpty()){

          Glide.with(image.context).load(url).centerCrop()
       .placeholder(R.drawable.no_imge)
                    .into(image)
        }
        else{
            image.setImageDrawable(placeHolder)
        }


    }
nithin joseph
  • 461
  • 3
  • 7
4

It would be more convenient to create a binding adapter which accepts multiple optional attributes so you can customize the loading request. Here's an example of such adapter.

@BindingAdapter(
  "srcUrl",
  "circleCrop",
  "placeholder",
  requireAll = false // make the attributes optional
)
fun ImageView.bindSrcUrl(
  url: String,
  circleCrop: Boolean = false,
  placeholder: Drawable?,
) = Glide.with(this).load(url).let { request ->

  if (circleCrop) {
    request.circleCrop()
  }

  if (placeholder != null) {
    request.placeholder(placeholder)
  }

  request.into(this)
}

And you can use it like this:

<ImageView
  ...
  app:srcUrl="@{someUrl}"
  app:placeholder="@{@drawable/ic_placeholder}"
  app:circleCrop="@{true}" />

You can also find an example in sources of the Owl - an official Android sample app on GitHub. See BindingAdapters.kt.

Valeriy Katkov
  • 33,616
  • 20
  • 100
  • 123
2
@JvmStatic
@BindingAdapter("glide")
fun glide(view: ShapeableImageView, url: String?) {
   if (!url.isNullOrEmpty()) {
      Glide.with(view).load(url).into(view)
   }
}
Williaan Lopes
  • 1,177
  • 15
  • 11
0

I think the best practise should be to create a separate variable for imageUrl of type string in layout.xml. BindingAdapter should be in model class. Also, the BindingAdapter method should be static as pointed out in comments. You can do it by wrapping inside a companion object with @JvmStatic annotation. For more details check this

    <variable
        name="imageUrl"
        type="String" />
GOVIND DIXIT
  • 1,748
  • 10
  • 27
  • 1
    To get imageurl at runtime we have to mentioned the path of the class where the url object is stored – yash786 Jul 04 '19 at 14:27
  • 1
    What class should bindinAdapter be in exactly? Model.kt, AlbumItemViewModel.kt, AlbumViewModel.kt, AlbumAdapter.kt? – Hasan Kucuk Jul 04 '19 at 14:43
  • 1
    BindingAdapter should be in model class – GOVIND DIXIT Jul 04 '19 at 14:46
  • : Required DataBindingComponent is null in class RowAlbumListBindingImpl. A BindingAdapter in com.myapp.app.model.AlbumList.Companion is not static and requires an object to use, retrieved from the DataBindingComponent. If you don't use an inflation method taking a DataBindingComponent, use DataBindingUtil.setDefaultComponent or make all BindingAdapter methods static. – Hasan Kucuk Jul 04 '19 at 14:55
  • Yes the bindingAdapter method should be static as already mentioned. – GOVIND DIXIT Jul 04 '19 at 14:57
0

use app:glideSrc like this

           <ImageView
                android:id="@+id/sender_profile_image_view"
                android:layout_width="@dimen/email_sender_profile_image_size"
                android:layout_height="@dimen/email_sender_profile_image_size"
                android:contentDescription="@string/email_sender_profile_content_desc"
                android:scaleType="centerCrop"
                app:glideCircularCrop="@{true}"
                app:glideSrc="@{email.sender.avatar}"
                app:layout_constraintTop_toTopOf="@id/sender_text_view"
                app:layout_constraintBottom_toBottomOf="@+id/recipient_text_view"
                app:layout_constraintEnd_toEndOf="parent"
                tools:src="@drawable/avatar_3" />

and in BindingAdapter

@BindingAdapter(
  "glideSrc",
  "glideCenterCrop",
  "glideCircularCrop",
  requireAll = false
)
fun ImageView.bindGlideSrc(
  @DrawableRes drawableRes: Int?,
  centerCrop: Boolean = false,
  circularCrop: Boolean = false
) {
  if (drawableRes == null) return

  createGlideRequest(
    context,
    drawableRes,
    centerCrop,
    circularCrop
  ).into(this)
}

private fun createGlideRequest(
  context: Context,
  @DrawableRes src: Int,
  centerCrop: Boolean,
  circularCrop: Boolean
): RequestBuilder<Drawable> {
val req = Glide.with(context).load(src)
  if (centerCrop) req.centerCrop()
  if (circularCrop) req.circleCrop()
     return req
}
Moumen Lahmidi
  • 462
  • 5
  • 7
0

Layout:

<data>
     <import type="com.example.package.R"/>
</data>

<ImageView
     android:id="@+id/logo"
     android:contentDescription="@string/logo"
     android:scaleType="fitCenter"
     app:gif = "@{R.drawable.logo}" />

Data Binding Utils:

@BindingAdapter("gif")
fun ImageView.setGif(res: Int) {
    Glide.with(this).load(res).into(this);
}
H.Step
  • 95
  • 1
  • 10