MVVM e DataBinding

Introduzione

Il patten MVVM si può considerare come un evoluzione dell’ MVC in quanto il concetto di Model e di View è pressochè identico, la differenza tra i due pattern risiede tra Controller e ViewModel.

Model

Il Model rappresenta la parte che contiene i dati, i vari possibili stati del dato e della logica relativa a quel dato.

View

La View fondamentalmente è la UI ed è collegata al ViewModel con il DataBinding, è possibile collegare a molte View un singolo ViewModel.

ViewModel

Il ViewModel è la parte che si occupa di preparare il dato per essere mostrato nella View, in pratica prepara tutti gli observable necessari ad un corretto funzionamento della View e mette a disposizione degli eventi che poi potranno essere riusati dalla View. Ovviamente un ViewModel può essere riutilizzato in più View.

DataBinding

Il DataBinding è la cosa che rende unico e particolare questo pattern, in pratica consente di collegare la View con il ViewModel attraverso dei tag e degli attributi presenti nei vari file di layout. Grazie a queto meccanismo è possibile esporre alla View degli Observable, gli Observable sono un tipo di dato che quando subiscono un cambiamento notificano alla view che qualcosa è successo, la view a quel punto sa che deve aggiornarsi con il nuovo valore presente nell observable.

Come si usa il DataBinding

Per prima cosa va abilitato il DataBinding nel gradle

Configurazione

android {
    dataBinding {
    enabled true
    }
}

Il secondo passo fondamentale è che la root di ogni layout di cui si vuol fare DataBinding sia <layout>

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

</layout>

il tag <data> serve a contenere le variabili che verranno usata nel layout, vediamo come funziona con le variabili statiche:

<data>
    <variable name="nome" type="String"/>
    <variable name="cognome" type="String"/>
    <variable name="user" type="it.monk.User"/>
</data>

<TextView android:text="@{user.stato}" ... />
<TextView android:text="@{nome}" ... />
<TextView android:text="@{cognome}" ... />

facendo così qualsiasi cosa ci sarà nella variabile nome si vedrà all’interno della prima TextView, mentre il cognome comparirà nella seconda ecc. Per valorizzare le variabili nell’ onCreate dell’activity è necessario richiamare i vari set:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
    ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    User user = new User();
    user.setStato("Stato");
    binding.setUser(user);
    binding.setNome("Nome");
    binding.setCognome("Cognome");
}

Così facendo però il DataBinding è poco utile e pratico, ed è qui che si inizia ad usare in aggiunta al ViewModel, come prima cosa modifichiamo il layout per far si che venga usato il ViewModel invece della variabili statiche

<data>
    <variable name="viewModel" type="it.monk.viewmodels.TextViewModel"/>
</data>

<TextView android:text="@{viewModel.user.stato}" ... />
<TextView android:text="@{viewModel.nome}" ... />
<TextView android:text="@{viewModel.cognome}" ... />

Come seconda cosa dobbiamo cambiare il codice per poter usare il ViewModel invece che le singole variabili:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
    ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    binding.viewmodel = new TextViewModel("Nome", "Cognome", "Stato");
}

da questo momento è possibile collegare i vari eventi a dei metodi presenti nel ViewModel, ad esempio se vogliamo prendere il tap su un bottone ci basterà inserire questo pezzo di codice nell’xml del pulsante

android:onClick="@{() -> viewmodel.onClickPressed()}"

un’altra cosa molto utile è la possibilità di creare dei parametri custom per le View, questo ci permette di poter fare binding di una url all’interno di un’ImageView e gestire tutta la logica di download e visualizzazione da codice

<ImageView app:imageUrl="@{viewModel.imageUrl}" />

nel ViewModel invece avremmo il seguente metodo:

@BindingAdapter({"imageUrl"})
public static void loadImage(ImageView view, String url) {
    //codice per download immagine
}

inoltre è anche possibile passare più parametri alla funzione, per esempio si potrebbe aggiungere un immagine di placeholder modificando il codice nel seguente modo:

<ImageView app:imageUrl="@{viewModel.imageUrl}" app:error="@{@drawable/placeholder}"/>

@BindingAdapter({"imageUrl", "error"})
public static void loadImage(ImageView view, String url, Drawable error) {
    //codice per download immagine
}

è inoltre possibile fare molte cose con il databinding come ad esempio dei controlli direttamente nell’ xml tipo:

<data>
    <import type="android.view.View"/>
    <variable name="viewModel" type="it.monk.viewmodels.TextViewModel"/>
</data>

<TextView android:visibility="@{viewModel.user.isActive ? View.Visible : View.Gone}"/>
../_images/magic.gif

per tutti gli altri comandi e le altre magie vi rimando alla documentazione di google ;)