Data Binding / LinearLayoutManager / ViewHolder

Data Binding – Part 2

At Google I/O 2015 a number of new Android libraries and tools were announced. One of them was the all new Data Binding library and in this series we’ll take a look at the library and explore some of the powerful features it provides.

Previously we got a basic Twitter client working, but the simplicity of the ViewHolder implementation made it a little difficult to quite understand why. So we’ll take a look at what the Data Binding library is doing for us and how it actually works.

The first thing to mention is that we need to add a dependency to the buildscript of the build.gradle in our top level project:

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.3.0'
        classpath "com.android.databinding:dataBinder:1.0-rc1"
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

In the build.gradle for the Android application sub-project we apply a build plugin:

apply plugin: 'com.android.application'
apply plugin: 'com.android.databinding'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.0"

    defaultConfig {
        applicationId "com.stylingandroid.databinding"
        minSdkVersion 7
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"

        buildConfigField 'String', 'TWITTER_CONSUMER_KEY', "\"${twitterConsumerKey}\""
        buildConfigField 'String', 'TWITTER_CONSUMER_SECRET', "\"${twitterConsumerSecret}\""
        buildConfigField 'String', 'TWITTER_ACCESS_KEY', "\"${twitterAccessKey}\""
        buildConfigField 'String', 'TWITTER_ACCESS_SECRET', "\"${twitterAccessSecret}\""
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    packagingOptions {
        exclude 'META-INF/LICENSE.txt'
    }
    lintOptions {
        disable 'InvalidPackage'
    }
}

dependencies {
    compile 'com.android.support:support-annotations:23.0.0'
    compile 'com.android.support:design:23.0.0'
    compile 'com.android.support:appcompat-v7:23.0.0'
    compile 'com.android.support:recyclerview-v7:23.0.0'
    compile 'org.twitter4j:twitter4j-core:4.0.4'
    compile 'org.twitter4j:twitter4j-async:4.0.4'
    compile 'com.github.bumptech.glide:glide:3.6.1'
}

An important thing to note here is that there is no compile dependency on the Data Binding library – it is simply a build level addition. During the build phase some additional support code will be added but there is no need to explicitly add any compile-time dependencies.

This should give something of a clue as to what may be happening. Essentially the Data Binding library is a code generation pass which is performed during the build and generates much of the boiler-plate code usually associated with binding data to Views.

In our case, it parses the layout files and detects the <layout> wrapper in layout/status_item.xml (which we discussed in the first article). Based upon the name of this file, it automatically generated a data binding class named StatusItemBinding and this is the mysterious class which we referenced in the StatusViewHolder implementation.

The Data Binding pass then strips out the <layout> wrapper leaving the RelativeLayout, and removes the Data Binding expressions as these are incorporated within the StatusItemBinding class – in other words it turns it back in to a standard Android XML layout file and adds a new Java class which implements the binding for us at runtime.

So let’s look again at StatusViewHolder and it may be a little clearer what is going on:

public class StatusViewHolder extends RecyclerView.ViewHolder {
    private StatusItemBinding binding;

    public StatusViewHolder(View itemView) {
        super(itemView);
        binding = DataBindingUtil.bind(itemView);
    }

    public void bind(Status status) {
        binding.setStatus(status);
    }

}

In the constructor we call DataBindingUtil.bind(itemView) which returns the StatusItemBinding which was generated at build time. Then in the bind(Status status) method we call a setter on the StatusItemBinding instance. This setter is generated from the <data> section in our original layout where we declared a variable of type .data.Status named status. When we call this setter it performs the necessary bindings which were extracted from the Data Binding expressions within the original layout.

Now that we understand a little better what is actually going on it should be clear how the basic app now works and why we see a list of tweets when we run the app:

Part1

While this is all very nice, for a simple layout with three items being bound, it hasn’t save us that much work – just some finding of views and some setText() calls on those TextViews. However we haven’t finished there – there is actually an awful lot more that Data Binding can do for us. Those who have been paying attention will have spotted that we extract an image URL from the Twitter feed, have an ImageView within the layout which is currently not being bound, and we’ve included a compile dependency for the Glide image loading library. In the next article we’ll look at how we can build automated image loading in to our Data Binding.

As this article has been mainly explanation, we haven’t actually added any additional code, but the code from the previous article is available here.

© 2015, Mark Allison. All rights reserved.

Copyright © 2015 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

Leave a Reply

Your email address will not be published. Required fields are marked *

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