Home · Android & Kotlin Tutorials

Data Binding in Android: Getting Started

In this Android Data Binding tutorial, you’ll learn how to link UI components to data in your app using a declarative format.

5/5 1 Rating

Version

  • Kotlin 1.3, Android 10.0, Android Studio 3.5

Linking code files with user interfaces is one of the most common tasks an Android developer faces. As simple as it sounds, this process causes many runtime errors and consumes a lot of time. Wouldn’t it be great if there was a library that took care of that?

The Data Binding Library is here to the rescue! Android introduced this library as part of the Jetpack suite of libraries at Google I/O 2018.

Data binding lets you handle the communication between views and data sources reliably and simply. Following this pattern is critical for many important architectures in Android development, particularly MVVM, currently one of the most used Android architectures.

In this tutorial, you’ll build GoBuy, a shopping list app, to learn how to integrate the Data Binding Library into existing Android projects. Along the way, you’ll learn:

  • How to enable data binding in your project
  • How to convert an existing layout file to one that works with data binding
  • How to work with code generated by the Data Binding Library in your Activities and Fragments
  • How to use data binding in RecyclerView and ViewHolder
Note: This tutorial assumes you know the basics of Android development with Kotlin. If you’re new to Kotlin, check out this Kotlin introduction tutorial. If you’re completely new to Android development, read through this Beginning Android Development tutorial to familiarize yourself with the basics.

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial and unzip it. Extract both begin and end folders from the zip file. Open the begin project by selecting Import project (Gradle, Eclipse ADT, etc) from the welcome screen of Android Studio 3.5 or later.

Wait for the Gradle sync to finish. Look at the project structure.

List of files including model, view and viewmodel for the project app, GoBuy.

As you can see in the screenshot above, the folder structure follows the MVVM architecture. There’s a file for the Model, another for the View and one for the ViewModel. During this tutorial, you’ll work with the files in the View folder, as well as the layout XML files in resources/layout, as shown in the screenshot below.

List of app layout files including activity grocery list, dialog and list item. .

Build and run. You’ll see the initial version of your shopping list.

Empty GoBuy screen with button to add items.

The app is functional and easy to navigate. Try adding a product by clicking the Add item button at the top-left corner of the screen. Fill in the dialog that appears and click the Save button. The item appears in the list with an option to edit or delete it.

Dialog for adding items asking for name, amount and price

Say you want to make sandwiches for your friends. You need to buy one loaf of bread, one jar of jam, two packages of cheese and a jar of mayonnaise. Add these to your shopping list.

List screen with bread, cheese, jam and mayonnaise.

Did you get a result similar to the screenshot above? Try playing with different values until you feel comfortable navigating through the app.

Before you dive into the project, it’s important to learn a little more about data binding.

Why Use Data Binding

Every developer wants clean and understandable code, but creating it is easier said than done. People rush, releases come one after another, clients want results and before you know it, your code is a mess.

Knowing this, the Android team decided to make things easier by standardizing development. To that end, they launched the Jetpack libraries, which include the Data Binding Library. This library offers several advantages:

  1. Less code: By keeping code in activities and fragments small, it helps you write cleaner and more readable code.
  2. Fewer errors: The binding is checked at compile time.
  3. Faster apps: Since the binding isn’t done in onCreate, your app runs faster.
  4. Safer connection between views and code: Because it doesn’t bind at runtime, it’s safer to get the UI components than findViewById().
  5. Safer connection between views and action: Data binding is safer than relying on onClick(), which can crash if you didn’t implement the requested method.
  6. Easier: Since it has less code and causes fewer errors, it’s easier to test and maintain.

Now that you understand the benefits, it’s time to discuss how data binding works.

How Data Binding Works

Data binding is about connecting views in the XML layout with data objects: In this case, Kotlin code. The Data Binding Library generates the classes needed for this process.

Layout XML files that use data binding are different because they start with a root layout tag followed by a data element and a view root element. This view root element is what your root would be in a non-binding layout file.

Each layout file then has its own generated data binding class. The Jetpack library does the job for you. By default, the class name is the name of the layout file in Pascal case and with the word binding at the end.

For example, in this project, activity_grocery_list.xml has the binding class ActivityGroceryListBinding.

Data binding has three main uses:

  • Showing data.
  • Handling user events.
  • Invoking actions on layout variables.

With that in mind, it’s time to prepare the project for data binding.

Configuring for Data Binding

First, you’ll configure the project for data binding. Open the build.gradle from the app directory. Locate the TODO comment at the bottom of this file and add the following code there:

dataBinding {
  enabled true
}

This tells Gradle that you want to build your project with the Data Binding Library.

Build and run. You’ll see the same screen you did in the beginning.

List screen with only bread listed.

Next, you’ll convert an existing XML layout to work with data binding.

Transforming a Standard XML Layout Into a Data Binding Layout

To convert a regular XML layout file to data binding layout, you need to follow three simple steps:

  1. Wrap your layout with a layout tag.
  2. Add layout variables (optional).
  3. Add layout expressions (optional).

You’ll repeat step one in each of the three XML layouts in the project. Start by opening activity_grocery_list.xml. You’ll find a TODO at the top of this file.

<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>
 </data>

You need to close this tag at the very bottom of the file. So, go to line 63, add a new line and close it with the following code:

</layout>

With this code added, this layout can use data binding. Otherwise, it would be a standard layout file and generate the automatic binding files.

Cleaning Up The Errors

The constraint layout tag now shows a warning regarding redundant declarations. To fix that, remove the three lines starting with xmlns:android, xmlns:app and xmlns:tools

Note: Android studio has a shortcut to create this layout tag: Control/Option + Enter or Right-click + Show context actions ▸ Convert to data binding layout.
Note: Notice the indentation isn’t right after adding these code snippets. To fix it, click the Code menu in Android Studio and select reformat code. This idents the code correctly.

Repeat the above steps once again in grocery_list_item.xml

Build and run. Try to add, edit and delete a few items and see that nothing crashes.

Grocery list screen with cheese listed, at a price of 50 and amount of 5.

Autogenerated Binding classes

Earlier, you learned that the Data Binding Library automatically generates classes for binding. Now, you can see the generated classes. Take a look at the screenshot below to see where they are in the file structure.

data binding files

Note: There’s a separate file for Java(generated) that contains different code instances. You won’t edit them in this tutorial, but it’s good to know they exist.

Enabling Data Binding in Activities, Fragments and Related Code

Now that the app and layouts know they’ll use data binding, you have to make changes to your code. As mentioned earlier, you’ll work with the three files inside the view folder.

Adding Data Binding to Activities

Open GroceryListActivity.kt. Notice there are several TODOs in the file. You’ll replace them from top to bottom as you learn how data binding configures the XML layouts with the code.

The first TODO, in line 53, tells you to remove the view items, which are Button, RecyclerView and TextView. All of these objects make reference to items in layout.activity_grocery_list. With the Data Binding Library, you don’t need to create variables for each one because the library does that for you.

Replace those three lines with the following line of code.

private lateinit var binding: ActivityGroceryListBinding

This code creates an instance of the automatically created binding class for activity_grocery_list.xml. Later, you’ll initialize through onCreate.

When you add this code, Android Studio asks you to import some dependencies. Do as suggested. Don’t worry about the errors because you’ll fix them in a minute.

import com.raywenderlich.android.gobuy.databinding.ActivityGroceryListBinding

By importing this class, you make a reference to the binding class from the previous step.

Binding the View

Next, replace the call to setContentView(R.layout.plain_activity) in onCreate with:

binding = DataBindingUtil.setContentView(this, R.layout.activity_grocery_list)

This code uses a Data Binding Library class called DataBindingUtil to set the content view. This line is very similar to the one you removed before, but now it associates the code with data binding.

3. Remove the findViewById lines of code.

Now you can remove the three lines with findViewById in onCreate, which are likely showing an error after your last change. You can remove these lines because you don’t need to look for the views by IDs since you’ll populate them through layout variables.

Before data binding, findViewById() was the most common way to connect the layout and code. It caused many problems because errors happened at runtime, and sometimes those errors weren’t detected until after production.

Accessing The View Children

Remove the remaining lines of code in onCreate and replace them with the following:

// 1
binding.rvGroceryList.layoutManager = LinearLayoutManager(this)

// 2
binding.rvGroceryList.adapter = GroceryAdapter(viewModel.groceryListItems, this, ::editGroceryItem, ::deleteGroceryItem)

// 3
binding.addItemButton.setOnClickListener {
  addGroceryItem()
}

Here’s a step-by-step breakdown:

  1. The first part binds the RecyclerView to the corresponding layout manager in the XML file. RecyclerViews are a separate topic, but on a high level, the linear layout manager is the item in the view that displays the list of grocery items.
  2. The RecyclerView also has an adapter which gets the items and assigns them to the layout
  3. Finally, you bind the addItemButton to its click method.

Are you a bit confused by all the TODO items? Take a look at the following screenshot to see how your onCreateshould look now:

Oncreate of activity after binding changes

Notice the last three methods still have errors. You’ll fix these in the next section.

Getting Rid of Errors

Replace the last lines inside deleteGroceryItem with:

//groceriesTotal.text = viewModel.getTotal().toString()
binding.rvGroceryList.adapter?.notifyDataSetChanged()

The line above tells the RecyclerView, which displays your list of groceries, that you removed an item in the list and it needs to refresh. You’ll fix the total amount later in the tutorial, so comment out that line for now.

You may notice the change needed for onDialogPositiveClick is very similar to the one you just did. Change the line with an error to:

binding.rvGroceryList.adapter?.notifyDataSetChanged()

This line tells the RecyclerView that whenever you click the add button in the alert, your list has changed.

As you did in the past method, comment out the code for the total until you come back to it later:

//groceriesTotal.text = viewModel.getTotal().toString()

You’re down to two errors in the file! Both refer to the addItemButton. As you might have guessed, you need to change those lines to make reference to the binding object. Replace the line with an error inside onDialogPositiveClick to:

Snackbar.make(binding.addItemButton, "Item Added Successfully",
        Snackbar.LENGTH_LONG).setAction("Action", null).show()

And the error in onDialogNegativeClick with:

Snackbar.make(binding.addItemButton, "Nothing Added",
        Snackbar.LENGTH_LONG).setAction("Action", null).show()

The difference in both these methods is now addItemButton is associated with the binding through binding.addItemButton.

You resolved all of the errors! You can finally test your changes.

Build and run. Test it as you did in the previous sections. Notice anything different?

Grocery

The app works as before, but the total doesn’t show. Don’t worry! This happens because of the two lines of code you commented out above. You’ll fix that next.

Adding Data Binding to Adapters

Next, you’ll connect GroceryAdapter to the binding classes, as you did for the GroceryListActivity. The process is pretty straightforward and involves very similar steps.

Open GroceryAdapter.kt and starting from the bottom, replace the whole ViewHolder with the following code:

class ViewHolder(val binding: GroceryListItemBinding) : RecyclerView.ViewHolder(binding.root) {
  fun bind(item: GroceryItem) {
    binding.apply {
      //itemName = "${item.amount}x: ${item.itemName}"
      //price = item.price.toString()
    }
  }
}

Look at the code before and after. Previously, the ViewHolder only made reference to the views. By applying the binding with the new code, you also set the value for the item and the price. Two lines have been commented out because they would throw errors that you’ll fix later.

After this change, onBindViewHolder stops recognizing the view objects because they don’t exist anymore. To fix that, replace the code in this method with the following:

holder.bind(items[position])
holder.binding.buttonEdit.setOnClickListener { itemEditListener(position) }
holder.binding.buttonDelete.setOnClickListener { itemDeleteListener(position) }

With this code, you assign the listeners to the edit and delete buttons. You also bind each item from the list in the recycler view.

Make one last change in the adapter. Replace the onCreateViewHolder code with:

val layoutInflater = LayoutInflater.from(context)
val binding: GroceryListItemBinding = DataBindingUtil.inflate(layoutInflater,
        R.layout.grocery_list_item,
        parent, false)
return ViewHolder(binding)

Since the adapter is a class you use to display each of the rows in the recycler view, and you tied the view holder to the binding classes, the layout now uses the binding. With this code, you create an instance of the view holder for the corresponding row through the binding classes.

Build and run. Test the app and add an item. Oh no! The list text is gone!

List screen with no items or total.

Don’t worry! In the next step, you’ll add the list and the total labels back to the screen with data binding.

Value Binding

Value Binding refers to the creation of variables for binding classes. With them, when anything changes in your Kotlin code, layouts automatically update as if they were directly observing the results of the code. There are two ways to update values: layout variables and layout expressions.

Layout Variables

Variables within the data tags you added at the top of the layout files describe a property within a data binding layout. If you aren’t sure which are the data tags, open any of the three layout files you modified at the beginning of this tutorial. Here’s a screenshot to help you spot them:

Code with data tags highlighted.

Layout Expressions

You can also write expressions through the so-called expression language. You can handle events in views and update or format the data accordingly.

Note: According to Android’s documentation, layout expressions should be small and simple because they can’t be unit tested and have limited IDE support. Instead, Android recommends using binding adapters. You won’t work with binding adapters in this tutorial; it’s a larger topic that could be its own tutorial.

Now that you’ve learned you can add values between the data tags in your layouts, you can see why the labels in your app stopped working. You’ll fix that next.

Binding Values in Activities

Go to activity_grocery_list.xml and locate your data tags at the top of the file. Add the following code between the data tags:

<variable name="totalAmount" type="String" />

By adding this variable, your Kotlin code now knows what value to observe for the total amount label that disappeared previously.

Locate a TODO at the bottom of the file. Notice android:text="" is empty. Between the quotes, reference the created variable:

android:text="@{totalAmount}"

With this reference, you let the layout know where to place the value of totalAmount.

Next, head back to GroceryListActivity and uncomment the following line:

binding.totalAmount = viewModel.getTotal().toString()

This tells the data binding object to locate the totalAmount you created in the layout and update it with the value returned from the getTotal method in the viewModel.

Find the other commented line and remove the comment so the totalAmount value gets assigned. It should look as follows:

binding.totalAmount = String.format("%.2f", viewModel.getTotal())

The code here also locates totalAmount and formats it to display up to two decimal points.

Build and run. Add an item and notice the total amount is there. Great start, but you still have to get the list labels back. Meanwhile, your app should look like this:

Grocery list screen with total label at the bottom of the screen.

Binding Values in Adapters

The steps for the adapter are similar to the ones in the activity you fixed. The same would be true for any other layout file, for example, a fragment.

First, open grocery_list_item.xml and locate the data tag at the top. Add these variables there:

<variable name="itemName" type="String"/>
<variable name="price" type="String" />

The first references the name of the item you added to your list, like bread or cheese. The second references the unit price, which is a number like 10 or 200.

Now, place the corresponding variables in the text attributes. You can locate them easily because they have TODO comments before them. For the item name, add this code:

android:text="@{itemName}"

The code above references the variable content you created in the data tags.

Do the same for the price:

android:text="@{price}"

In this case, you reference the value of the price variable from the data tags.

Open GroceryAdapter and locate the commented code. In this case, there is no need to change it, so remove the comment. Now it works!

Build and run. Your app is fully functional now!

Play around with it and ensure everything works as expected. As you can see in the screenshot, the user changed their mind about using jam in their sandwiches and switched it to ham. That’s why having an Edit button is handy.

Congratulations on getting here!

Compete grocery list with items, prices and list total.

Where to Go From Here?

Download the completed project files by clicking the Download Materials button at the top or bottom of this tutorial.

Congratulations on converting an existing app to use a more safe, modern approach to app development in Android using data binding.

If you want to learn more on this topic, take a look at:

If you have any suggestions, questions or if you want to show off what you did to improve this project, join the discussion below.

Average Rating

5/5

Add a rating for this content

1 rating

More like this

Contributors

Comments