It is based on the todo-mvp sample and uses the Data Binding library to display data and bind UI elements to actions.
It doesn't follow a strict Model-View-ViewModel or a Model-View-Presenter pattern, as it uses both View Models and Presenters.
The Data Binding Library saves on boilerplate code allowing UI elements to be bound to a property in a data model.
- Layout files are used to bind data to UI elements
- Events are also bound with an action handler
- Data can be observed and set up to be updated automatically when needed
In the todo-mvp sample, a Task description is set in the TaskDetailFragment:
public void onCreateView(...) {
...
mDetailDescription = (TextView)
root.findViewById(R.id.task_detail_description);
}
@Override
public void showDescription(String description) {
mDetailDescription.setVisibility(View.VISIBLE);
mDetailDescription.setText(description);
}
In this sample, the TaskDetailFragment simply passes the Task to the data binding:
@Override
public void showTask(Task task) {
mViewDataBinding.setTask(task);
}
and the library will take care of displaying it, as defined by the layout (taskdetail_frag.xml
)
<TextView
android:id="@+id/task_detail_description"
...
android:text="@{task.description}" />
Data binding eliminates the need to call findViewById()
and event binding can also help minimizing setOnClickListener()
.
In this CheckBox from taskdetail_frag.xml
, the presenter is called directly when the user taps on it:
<CheckBox
android:id="@+id/task_detail_complete"
...
android:checked="@{task.completed}"
android:onCheckedChanged="@{(cb, isChecked) ->
presenter.completeChanged(task, isChecked)}" />
The view that shows the list of tasks (TasksFragment) only needs to know if the list is empty to show the appropriate message in that case. It uses TasksViewModel to provide that information to the layout. When the list size is set, only the relevant properties are notified and the UI elements bound to those properties are updated.
public void setTaskListSize(int taskListSize) {
mTaskListSize = taskListSize;
notifyPropertyChanged(BR.noTaskIconRes);
notifyPropertyChanged(BR.noTasksLabel);
notifyPropertyChanged(BR.currentFilteringLabel);
notifyPropertyChanged(BR.notEmpty);
notifyPropertyChanged(BR.tasksAddViewVisible);
}
There are multiple ways to create the relevant parts of a feature using the Data Binding Library. In this case, the responsibility of each component in this sample is:
- Activity: object creation
- Fragment: interaction with framework components (options menu, Snackbar, FAB, Adapter for list…)
- Presenter: receives user actions and retrieves the data from the repository. If it doesn't do data loading, it's calling an action handler (See TasksItemActionHandler)
- ViewModel: Exposes data for a particular view
Some features don't have a ViewModel (TaskDetail, AddEditTask) as they use the Task model directly.
Data Binding Library.
As the Data Binding Library takes care of many of the wiring that would usually be unit tested, the number of unit tests is lower although the test coverage should be similar.
No difference with MVP.
Compared to MVP, there are more Java classes but less code per class. Because some wiring is moved to layouts, there are more XML lines.
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Java 50 1079 1552 3327 (3450 in MVP)
XML 34 122 337 714
-------------------------------------------------------------------------------
SUM: 84 1201 1889 4041
-------------------------------------------------------------------------------
Easier than MVP for small changes. A new feature might require some experience with the library.
The Data Binding library takes care of the communication between some components, so developers need to understand what it does and doesn't before making changes to the code.