Advanced Data Binding in Android: Layout Expressions

Learn how to use layout expressions for data binding in Android and make your code more concise and less error-prone. By Ivan Kušt.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 2 of this article. Click here to view the first page.

Handling Events from Views

Data binding allows you to handle view events too. You can link an event from view — like click to functions — using layout expressions.

There are two approaches:

  1. Method references: binding methods with matching signatures to view events.
  2. Listener bindings: lambda expressions that are evaluated when event is triggered.

Understanding Method References vs. Listener Bindings

The major difference between the two is method references create bindings at compile time, while listener bindings are evaluated at run time when the event is triggered. That means errors will be easier to detect when using method references because they are validated at compile time.

On the other hand, listener bindings provide increased flexibility because you can call any bound method from the lambda expression.

Handling Click Event Using Method Reference

You’ll test method references by creating and adding a method for handling click event on button for adding new items.

First, add a class that will hold the function that will be called on click.

Open GroceryListActivity and add the following class to it:

class Listeners(private val supportFragmentManager: FragmentManager) {

  fun onAddGroceryItemClick(view: View) {
    val newFragment = NewItemDialogFragment.newInstance(R.string.add_new_item_dialog_title, null)
    newFragment.show(supportFragmentManager, "newItem")
  }
}

Add imports for FragmentManager and View:

import android.view.View
import androidx.fragment.app.FragmentManager

Note that onAddGroceryItemClick needs to have one argument of type View. It needs to match onClick method signature from View.OnClickListener.

Open activity_grocery_list.xml and add the following variable in data tags:

<variable
    name="listeners"
    type="com.raywenderlich.android.gobuy.view.GroceryListActivity.Listeners" />

This binds the new Listeners class. Now you can bind the onAddGroceryItemClick from Listeners. Find Button with id add_item_button and add the following property to it:

android:onClick="@{listeners::onAddGroceryItemClick}"

This will wrap onAddGroceryItemClick in View.OnClickListener and set it on add_item_button.

Finally, bind the Listeners class in GroceryListActivity by adding the following to the end of onCreate:

binding.listeners = Listeners(supportFragmentManager)

Remove the code that sets the listener on addItemButton:

binding.addItemButton.setOnClickListener {
  addGroceryItem()
}

Your onCreate now looks like this:

override fun onCreate(savedInstanceState: Bundle?) {
  setTheme(R.style.AppTheme)

  super.onCreate(savedInstanceState)

  viewModel = ViewModelProviders.of(this).get(GroceryListViewModel::class.java)
  binding = DataBindingUtil.setContentView(this, R.layout.activity_grocery_list)

  binding.rvGroceryList.layoutManager = LinearLayoutManager(this)
  binding.rvGroceryList.adapter = GroceryAdapter(viewModel.groceryListItems, this, ::editGroceryItem, ::deleteGroceryItem)

  binding.total = viewModel.getTotal()
  binding.listeners = Listeners(supportFragmentManager)
}

Build and run the app and tap add button.

Shows Add new item dialog in app

You’ll see the dialog for adding an item.

Handling Events Via Listener Bindings

You’ll test listener bindings by adding listeners for item edit and delete button clicks. Open grocery_list_item.xml.

Add the following bindings in the data tags:

<variable
  name="position"
  type="Integer" />

<variable
    name="adapter"
    type="com.raywenderlich.android.gobuy.view.GroceryAdapter" />

This binds GroceryAdapter with listeners for edit and delete actions: itemEditListener and itemDeleteListener. Also, this binds the position that represents the position of the item being shown in the list. It will be passed to listeners from GroceryAdapter.

Find Button with id button_edit and add the listener binding for onClick event:

android:onClick="@{() -> adapter.itemEditListener.invoke(position)}"

The lambda expression invokes itemEditListener and passes the position of the item.

Find Button with id button_delete and add the listener binding for onClick event:

android:onClick="@{() -> adapter.itemDeleteListener.invoke(position)}"

Finally, open GroceryAdapter and add the following line to onCreateViewHolder:

binding.adapter = this

This assigns the adapter instance so its listeners can be called in onClick methods.

Build and run the app and add a few items. Try to delete one. What’s going on?

The app crashes after you tap edit or delete buttons for items. That’s because you didn’t bind the position of the item. This is one of the drawbacks because listener bindings are validated at runtime.

Add the following line in bind function of ViewHolder:

binding.position = adapterPosition

This assigns position of the item that was bound to the view.

Build and run the app.

Main screen with total set to zero

You can again edit and remove items from the list.

That’s it! You’ve mastered layout expressions. Use them wisely!

Where to Go From Here?

You can download the completed project files by clicking on the Download Materials button at the top or bottom of the tutorial.

Check out Data Binding tutorial if you haven’t. If you want to know more about Layout expression take look at the official documentation.

We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!