Skip to main content
  1. Posts/

Android DOs: Data Binding adapters is a good place for custom view logic

· loading · loading · ·

Android DOs: Data Binding adapters is a good place for custom view logic
#

TL;DR Custom view logic is present in almost any app, and Android Data Binding adapter is a better place for it rather than Activity, Fragment, etc., because it helps to decouple application code from framework glue code.

Android Data Binding is a truly great library: not only does it simplify use of the ViewModel pattern on Android, but also has “binding adapters”, which allows to execute custom view logic around Android widgets.

Typically this type of logic is scattered across Activitys, Fragments, etc. and also being duplicated here and there in a codebase.

Data Binding adapters allow to move this custom view logic out of application code, to the side of framework glue code. This results in separation of concerns, cleaner architecture, encapsulation of functionality, allows to keep changes (of custom view logic) in one place and draws a clear boundary between you app and the Android framework.

Using Data Binding adapters by example of downloading image into ImageView
#

Even with help of great libraries such as Glide and Picasso, there is still some ceremony involved, before android:src of ImageView is populated.

A solution would be to introduce a binding adapter which implements image download, transformation and assignment logic.

In the example below, bindSrcUrl would download image from a URL, while providing placeholder for in progress and failed cases.

bindSrcUrlPlaceholderLetters would download image from a URL, but generates placeholder out of string with letters (this is for user avatars).

object ImageViewBindingAdapters {

    @BindingAdapter("src", "placeholder", "placeholderTint")
    @JvmStatic fun bindSrcUrl(imageView: ImageView, url: String, placeholder: Drawable, placeholderTint: Int) {
        val tintedPlaceholder = tintPlaceholder(placeholder, placeholderTint)
        val loadRequest = requestFromUrl(imageView.context, url)

        //`fitWithCenterCrop` is an extension function loads and resizes image
        imageView.fitWithCenterCrop(loadRequest, tintedPlaceholder)
    }

    @BindingAdapter("src", "placeholderLetters", "placeholderTint")
    @JvmStatic fun bindSrcUrlPlaceholderLetters(imageView: ImageView, url: String, placeholderLetters: String, placeholderTint: Int) {
        val context = imageView.context
        val tint = ContextCompat.getColor(context, placeholderTint)
        val letterPlaceholder = LetterDrawable(context, placeholderLetters, tint)

        val loadRequest = requestFromUrl(imageView.context, url)
        imageView.fitWithCenterCrop(loadRequest, letterPlaceholder)
    }

    private @JvmStatic fun tintPlaceholder(placeholder: Drawable, tint: Int) =
            DrawableCompat.wrap(placeholder).apply {
                DrawableCompat.setTint(this, tint)
                DrawableCompat.setTintMode(this, PorterDuff.Mode.SRC_ATOP)
            }

    ...
}

Adapters can be used like so:

<ImageView
  ...
  bind:placeholder="@{@drawable/ic_item_black_36dp}"
  bind:placeholderTint="@{@color/secondaryAccent}"
  bind:src="@{viewModel.itemImageUrl}"/>

That’s all – now we can apply these adapters anywhere in application. Whenever there is a change in how image is downloaded, or new features such as image transformations are added, those would be applied centrally and wouldn’t affect business logic of your application, because would reside on a side of framework glue code.

Summary
#

In this article we’ve learned how Android Data Binding adapters help a lot for code separation and reusability: code in adapters is decoupled from your application logic, it is encapsulated well and can be reused across different applications.

Your name here
Author
Your name here
A little bit about you