Kotlin - Android

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

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 1 vote
Article Rating
Load Image url on ImageView by using data binding with Picasso in Kotlin

Recent Posts

Hide your production API key or any sensitive data in Android

Hi everyone, In this article, we are going to learn how to hide the production… Read More

2 years ago

How to handle the localisation or multi language support in android with examples?

Hello everyone, Today in this article, we are going to learn about localisation to support… Read More

2 years ago

How to convert any callback to Coroutines and use them in Kotlin Android?

Hello everyone, In this article, we are going to learn something to handle the callback… Read More

2 years ago

Request Permission Launcher with Kotlin in Android

In this article, we are learning about the run time permissions for request permission launchers.… Read More

2 years ago

Implement the SMS User Consent API and SMS Retriever API in Android

Hello everyone. In my last tutorial, we learned about the Jetpack Compose introduction and about applying the… Read More

3 years ago

Jetpack Compose Coroutine flow with LiveData/ViewModel in Android

Hello everyone, In this article, we are going to learn about the Jetpack Compose with… Read More

3 years ago