Sitemap

Data Binding & Binding Adapters

--

Modern Android development involves a variety of tools and libraries to streamline and enhance the development process. Among these, Data Binding stands out for its ability to bind UI components to data sources declaratively. In this article, we’ll dive into how you can use Data Binding along with Retrofit and LiveData to create a seamless and efficient Android application.

Why Use Data Binding?

Data Binding allows you to:

  1. Reduce boilerplate code: You don’t need to manually find views and update them.
  2. Improve performance: Data Binding minimizes the need for UI framework calls.
  3. Simplify code maintenance: Your UI code becomes cleaner and more modular.

Project Overview

We’ll build a simple application that fetches random dog images from the Dog CEO API and displays them using Data Binding, Retrofit for network requests, and LiveData for handling UI-related data.

Step 1: Setting Up the Binding Adapter

To start, we need a Binding Adapter to load images into an ImageView using Picasso. This adapter will help us bind a URL directly to an ImageView in our layout.

package com.android.databindingdemo.view

import android.widget.ImageView
import androidx.databinding.BindingAdapter
import com.squareup.picasso.Picasso

object BindingAdapter {

@JvmStatic
@BindingAdapter("app:image")
fun loadImage(view: ImageView, url: String?) {
url?.let {
Picasso.get().load(url).into(view)
}
}
}

Get Abhishek Pathak’s stories in your inbox

Join Medium for free to get updates from this writer.

Step 2: Setting Up Retrofit

Next, let’s configure Retrofit to handle network requests. Retrofit is a powerful HTTP client for Android that makes it easy to consume RESTful web services.

package com.android.databindingdemo.data.remote

object Constant {
const val BASE_URL = "https://dog.ceo/api/breeds/image/"
const val END_POINT = "random"
}

data class DogResponse(
val message: String,
val status: String
)

interface ApiService {
@GET(Constant.END_POINT)
fun getRandomDogs(): Call<DogResponse>
}

object RetrofitBuilder {
private lateinit var retrofit: Retrofit

fun getRetrofit(): Retrofit {
if (!this::retrofit.isInitialized) {
retrofit = Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return retrofit
}
}

Step 3: Creating the ViewModel

The ViewModel will manage UI-related data in a lifecycle-conscious way. It will fetch data from the API and expose it to the UI through LiveData.

package com.android.databindingdemo.viewmodel

import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.android.databindingdemo.data.remote.ApiService
import com.android.databindingdemo.data.remote.DogResponse
import com.android.databindingdemo.data.remote.RetrofitBuilder
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class DogViewModel : ViewModel() {

private var _dogLiveData = MutableLiveData<DogResponse>()
val dogLiveData: LiveData<DogResponse> = _dogLiveData

private lateinit var apiService: ApiService

fun fetchRandomDog() {
val retrofit = RetrofitBuilder.getRetrofit()
apiService = retrofit.create(ApiService::class.java)

apiService.getRandomDogs().enqueue(object : Callback<DogResponse> {
override fun onResponse(call: Call<DogResponse>, response: Response<DogResponse>) {
if (response.isSuccessful) {
_dogLiveData.value = response.body()
}
}

override fun onFailure(call: Call<DogResponse>, t: Throwable) {
Log.i("tag", t.message.toString())
}
})
}
}

Step 4: Setting Up the Activity

Finally, we set up our MainActivity to use Data Binding and ViewModel. The ActivityMainBinding class is generated from our layout file, which we will see shortly.

package com.android.databindingdemo.view

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import com.android.databindingdemo.R
import com.android.databindingdemo.databinding.ActivityMainBinding
import com.android.databindingdemo.viewmodel.DogViewModel

class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var dogViewModel: DogViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.lifecycleOwner = this
dogViewModel = ViewModelProvider(this)[DogViewModel::class.java]
binding.viewmodel = this.dogViewModel
}
}

Step 5: Creating the Layout

In the layout file activity_main.xml, we set up the UI components and bind them to the ViewModel.

<?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="viewmodel"
type="com.android.databindingdemo.viewmodel.DogViewModel" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.MainActivity">

<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imgDog"
android:layout_width="match_parent"
android:layout_height="300dp"
android:scaleType="centerCrop"
app:image = "@{viewmodel.dogLiveData.message}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/txtDogUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:text="@{viewmodel.dogLiveData.message}"
app:layout_constraintTop_toBottomOf="@+id/imgDog" />

<Button
android:id="@+id/btnDog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Next random dog"
android:onClick="@{() -> viewmodel.fetchRandomDog()}"
app:layout_constraintBottom_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Conclusion

By combining Data Binding with Retrofit and LiveData, we can create a clean, efficient, and maintainable Android application. This approach reduces boilerplate code, ensures a clear separation of concerns, and enhances the overall user experience. Whether you’re a seasoned developer or just starting, integrating these tools into your projects will significantly improve your development process. Happy coding!

--

--

Abhishek Pathak
Abhishek Pathak

Written by Abhishek Pathak

Associate Director @ UBS | 12 Years in Software Engineering | Mobile Architecture | Tech Leader, Mentor & Writer