Data Binding Events
In the last post we discussed the basics of Android data binding and how to synchronize session state and screen state by triggering a UI change when there is a change in the domain model.
In this post we’re going to take this concept one step further using data binding events to trigger a change in the domain model.
Handling events
Android data binding allows binding Java classes to UI events including onClick
, onLongClick
, onTextChanged
, etc
activity_main.xml
<data>
<variable
name="result"
type="com.example.cleancalc.Result"/>
<variable
name="presenter"
type="com.example.cleancalc.MainPresenter"/>
</data>...<EditText
android:id="@+id/input_text_1"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:gravity="center"
android:inputType="numberSigned"
android:onTextChanged="@{presenter::onInput1TextChanged}"
/>
<EditText
android:id="@+id/input_text_2"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:gravity="center"
android:inputType="numberSigned"
android:onTextChanged="@{presenter::onInput2TextChanged}"
/>
Similar to binding the result value, we introduce a new variable to the layout file for the presenter. Then we bind the onTextChanged
event to methods declared in the presenter.
MainPresenter.kt
fun onInput1TextChanged(s: CharSequence, start: Int, before: Int,
count: Int) {
calc.addend1 = if (isNumber(s)) s.toString().toInt() else 0
setColor()
}
fun onInput2TextChanged(s: CharSequence, start: Int, before: Int,
count: Int) {
calc.addend2 = if (isNumber(s)) s.toString().toInt() else 0
setColor()
}
Now any text change event in the UI triggers an immediate change in the domain model. Since our result value also uses data binding, when the new result is calculated in the model this triggers an immediate update in the UI.
Now we have bi-directional data binding.
Do I still need a presenter with data binding?
If data binding allows us to bind views layer directly to domain models why do we even need a presenter?
Presenters are for presentation logic. It’s very likely there is some logic associated with the display that is separate from the domain logic.
For example setting the color of the result.
Result.kt
data class Result(var value: ObservableInt, var category: Category)enum class Category {
LOW,
HIGH
}
The domain model Result
only cares about whether the value is LOW
or HIGH
, since the color coding logic is associated with presentation.
MainPresenter.kt
private fun setColor() = controller.setResultColor(
if (result.category == Category.HIGH)
Color.GREEN else Color.RED)enum class Color {
RED,
GREEN
}
The presenter then translates the LOW
or HIGH
category into color codes RED
or GREEN
.
MainActivity.kt
override fun setResultColor(color: Color) {
val colorInt = if (color == Color.GREEN)
getColor(R.color.green) else getColor(R.color.red)
result_text.setTextColor(colorInt)
}
Then since we want to keep Android dependencies completely out of the presenter, we rely on the activity to actually load the appropriate color resource values and set the text color.
Data binding is a classic software design principle we can use alongside clean architecture to reduce boilerplate and synchronize state. In the next post we’ll look at the new LiveData
class from Android Architecture Components and see how it is similar and different to data binding.
This post is part of a series on clean architecture that explores how classic software design principles can be applied to modern Android development.
If you found this article helpful, please give it some applause 👏 to help others find it. Feel free to leave a comment below.