Load Image url on ImageView by using data binding with Picasso in Kotlin

Sharing is caring!

While building the android app by using the MVVM architecture design pattern with Kotlin, I faced many of issue for example data binding and ViewModel communication etc. I am sharing one of the issues which I faced to load image URL on image view with help of Picasso. I thought It would be worth to share with everyone to understand this issue and the solution.

First of all, using the data binding concept we need to add dependency inside the build.gradle file and enable the data binding feature.

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 26
    ....................................
    dataBinding {
        enabled = true
    }
}

dependencies {
    ............................................
    compile 'com.squareup.picasso:picasso:2.5.2'

    kapt 'com.android.databinding:compiler:2.3.2'
}

kapt {
    generateStubs = true
}

For the data binding concept, I used the ObservableFields Object. ObservableFields are self-contained observable objects that have a single field. The primitive versions avoid boxing and unboxing during access operations.

class TimelineItemViewModel {

    private val TAG = javaClass.simpleName

    var user  = ObservableField<String>()
    var imageUrl= ObservableField<Drawable>()
}

Now I have to bind these two fields with the help of data binding in XML. Let see.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable name="vm" type="com.sunil.twitterkotlinmvvm.viewmodel.TimelineItemViewModel" />
    </data>

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="4dp"
        android:clickable="true"
        app:cardCornerRadius="5dp"
        app:cardElevation="2dp">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp">

            <ImageView
                android:id="@+id/avatar"
                android:layout_marginRight="3dp"
                android:layout_width="@dimen/avatar_size"
                android:layout_height="@dimen/avatar_size"
                android:layout_marginTop="@dimen/small_spacing"
                android:src="@{vm.imageUrl}"
                android:contentDescription="@string/content_description_avatar" />

            <LinearLayout
                android:layout_toRightOf="@+id/avatar"
                android:id="@+id/header"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginBottom="@dimen/small_spacing"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/user"
                    style="@style/NormalText"
                    android:text="@{vm.user}"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    tools:text="Name" />

            </LinearLayout>

        </RelativeLayout>
    </android.support.v7.widget.CardView>
</layout>

Here You need to focus on two things  <data> tag and  android:src=”@{vm.imageUrl}” tag. ImageUrl is the  ObservableFields of TimelineItemViewModel class.

The magic happens when we create a custom Picasso target that takes the ObservableField and handles updating its value. Let’s create the BindableFieldTarget to load the bitmap.

public class ProfileViewModel(context: Context, imageUrl : String) {

    var IMAGE_URL: String? = null
    var profileImage: ObservableField<Drawable>? = null
    private var bindableFieldTarget: BindableFieldTarget? = null

    init {
        this.IMAGE_URL = imageUrl
        profileImage = ObservableField<Drawable>()
        // Picasso keeps a weak reference to the target so it needs to be stored in a field
        bindableFieldTarget = BindableFieldTarget(profileImage!!, context.getResources())
        Picasso.with(context)
                .load(IMAGE_URL)
                .placeholder(R.drawable.user)
                .into(bindableFieldTarget);
    }

    class BindableFieldTarget(private val observableField: ObservableField<Drawable>, private val resources: Resources) : Target {

        override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) {
            observableField.set(BitmapDrawable(resources, bitmap))
        }

        override fun onBitmapFailed(errorDrawable: Drawable) {
            observableField.set(errorDrawable)
        }

        override fun onPrepareLoad(placeHolderDrawable: Drawable) {
            observableField.set(placeHolderDrawable)
        }
    }
}

Now we almost were done. One thing that we need to set this imageUrl to this target in our adapter class or whatever you are using.

override fun onBindViewHolder(holder: TimelineViewHolder?, position: Int) {
    // - get element from your dataset at this position & replace the contents of the view with that element

    holder.view.vm.user.set(items[position].user.screenName)
    val profileViewModel = ProfileViewModel(MainApplication.applicationContext(), items[position].user.profileImageUrl)
    holder.view.vm.imageUrl.set(profileViewModel.profileImage!!.get())
  
}

Great, We did so for. In my next tutorial, we will focus on MVVM architecture design pattern to used to build an awesome android application. If you are wondering the MVP design pattern with Java and Rx, then please check this post would be helpful or MVP with RxJava with Kotlin. Here is the Github link for the source code of the above sample.

Please do subscribe email to get all newsletters of this blog and if you feel that this post will help you to better understand then do not forget to subscribe, share and comment below. Ok, then I will see you in my next tutorial till then enjoy your life and happy coding 🙂

5 2 votes
Article Rating

Similar Posts

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments