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 🙂
I am a very enthusiastic Android developer to build solid Android apps. I have a keen interest in developing for Android and have published apps to the Google Play Store. I always open to learning new technologies. For any help drop us a line anytime at contact@mobologicplus.com