Advanced Data Binding in Android: Layout Expressions [FREE]
source link: https://www.raywenderlich.com/27762347-advanced-data-binding-in-android-layout-expressions
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
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.
Version
In this tutorial, you’ll learn to use layout expressions for data binding in Android. It enables you to do some powerful stuff when binding layouts and makes your code more concise and less prone to error.
You’ll work on the Go-Buy application from Data Binding Getting started tutorial. It’s a simple app for making a shopping list. You’ll refactor it slightly and learn:
- Which mathematical expressions you can use with data binding.
- How to move simple logic from code to layout expressions.
- An easy way to reference strings and pass them arguments in data binding.
- How to handle view events like clicks with data binding.
Getting Started
Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial. Unzip it and import the starter project into Android Studio.
Wait for the Gradle sync to finish.
The project structure follows Model-View-Viewmodel (MVVM) architecture. There is one model GroceryItem
that represents an item in the shopping list. GroceryListActivity
shows the shopping list using GroceryAdapter
. NewItemDialogFragment
shows a dialog for adding an item to the shopping list.
You continue where Getting Started tutorial left. Before you get your hands dirty, what was it that data binding does?
Recapping Data Binding
Android Data Binding Library is part of Android Jetpack and it simplifies binding your data classes to layout. You bind your classes in the layout XML file and set view properties there. That approach isolates the logic for showing data on views in one place. Using the traditional “findViewById
” approach of connecting data and view, you would call methods on views from code to set the data shown to user. This can be error-prone. Using data binding reduces the amount of code and the risk of bugs.
To use data binding in your layout, the root tag must be layout
. It must contain two elements:
-
data
element: used to describe data that will be shown in layout. -
view
root element: this would be the root tag in a “regular” layout.
Data defined in the data element are used in expressions when setting the view properties using @{}
syntax.
Seeing The Difference to View Binding
The Android documentation for Data Binding points to the main difference: “If you are using data binding primarily to replace findViewById()
calls, consider using view binding instead.”
View Binding Library simplifies access to views from your Activities or Fragments.
Data Binding Library gives access to data classes in your layout files. Instead of setting data on each view from code, you pass the data class and set view properties in XML.
Understanding Layout Expressions
Data Binding Library provides language for writing expressions in your layouts. This includes mathematical operations, concatenating, formatting and using strings from resources. This should be enough to implement a simple logic for showing and formatting your data within layouts. Be careful. This can be double-edged – adding too complex logic using layout expressions can make them unreadable. With great power comes great responsibility!
Using Mathematical and String Operators
The simplest and most used are mathematical expressions. Supported mathematical operators are + - / * %
.
To test this you’ll move logic for calculating total price of items from GroceryAdapter
to R.layout.grocery_list_item
.
Open R.layout.grocery_list_item
and replace data
element them with:
<data> <variable name="item" type="com.raywenderlich.android.gobuy.model.GroceryItem" /> </data>
This enables you to bind GroceryItem
objects that hold data for adapter items to layout and calculate the amount for an item (row in the Go-Buy item list).
Find TextView
with id tv_grocery_item_name
and replace android:text
property with:
android:text='@{item.itemName + ": " + item.amount + "x"}'
This sets the text to item name followed by the amount, for example: Bread: 2x
.
Note two critical items. First, the strings in layout expressions can be concatenated in a way similar to concatenating strings in code. Second, if you are using strings in layout expressions, open them with single quotes (‘) in order to use double quotes (“) inside the expression.
Next, add layout expression for showing single-item price and total price (item price multiplied by number of items). This time you’ll use String.format()
to format prices.
Find TextView
with id tv_grocery_item_price
and again replace android:text
property with:
android:text='@{"$" + String.format("%.2f", item.price) + "/$" + String.format("%.2f", item.price * item.amount)}'
Let’s go over this expression.
You are concatenating four parts: dollar sign, single-item price, dollar sign and total price.
Each price is formatted using String.format()
.
You can use most expressions that you can use in code. For a detailed list check the official documentation.
In order for the new layout expression to work, you need to pass GroceryItem
instance to GroceryListItemBinding
. Open GroceryAdapter
and change bind
function from ViewHolder
to:
fun bind(item: GroceryItem) { binding.item = item }
Build and run the app and tap “Add item” to add a new item to the shopping list:
After adding, you can see the prices formatted using layout expressions.
Using String Resources With Parameters
You can also use strings from resources along with parameters in layout expression.
Open activity_grocery_list.xml
and replace the definitions in data
tags with:
<data> <variable name="total" type="Double" /> </data>
Find TextView
with id total_text_view
and replace android:text
property with:
android:text="@{@string/money_amount(total)}"
This expression is equal to using getString(R.string.money_amount, total)
in code.
Open GroceryListActivity
and replace assignments of binding.totalAmount
with:
binding.total = viewModel.getTotal()
deleteGroceryItem
will look like:
private fun deleteGroceryItem(position: Int) { Log.d("GoBuy", "delete") viewModel.removeItem(position) binding.total = viewModel.getTotal() binding.rvGroceryList.adapter?.notifyDataSetChanged() }
and onDialogPositiveClick
:
override fun onDialogPositiveClick(dialog: DialogFragment, item: GroceryItem, isEdit: Boolean, position: Int?) { if (!isEdit) { viewModel.groceryListItems.add(item) } else { viewModel.updateItem(position!!, item) binding.rvGroceryList.adapter?.notifyDataSetChanged() } binding.total = viewModel.getTotal() Snackbar.make(binding.addItemButton, "Item Added Successfully", Snackbar.LENGTH_LONG).setAction("Action", null).show() }
Build and run the app and add some items.
Whoa! What’s that weird value displayed as total? That’s because total
variable isn’t set initially.
To fix it, add the following at the end of onCreate
:
binding.total = viewModel.getTotal()
Build and run again – the total value is now displayed properly.
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:
- Method references: binding methods with matching signatures to view events.
- 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.
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.
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!
raywenderlich.com Weekly
The raywenderlich.com newsletter is the easiest way to stay up-to-date on everything you need to know as a mobile developer.
Get a weekly digest of our tutorials and courses, and receive a free in-depth email course as a bonus!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK