Modello di progettazione MVVM di Android

In questo tutorial, discuteremo e implementeremo il Modello Architetturale MVVM per Android nella nostra Applicazione Android. Abbiamo precedentemente discusso il Modello MVP per Android.

Perché abbiamo bisogno di questi pattern? Aggiungere tutto in un’unica Activity o Fragment porterebbe a problemi nel testing e nel refactoring del codice. Pertanto, è consigliato l’uso della separazione del codice e un’architettura pulita.

Android MVVM

MVVM sta per Modello, Visualizzazione, ViewModel.

  • Modello: Questo contiene i dati dell’applicazione. Non può parlare direttamente con la Visualizzazione. Generalmente, è consigliato esporre i dati al ViewModel tramite Observables.
  • Visualizzazione: Rappresenta l’interfaccia utente dell’applicazione priva di qualsiasi logica dell’applicazione. Osserva il ViewModel.
  • ViewModel: Agisce come un collegamento tra il Modello e la Visualizzazione. È responsabile della trasformazione dei dati dal Modello. Fornisce flussi di dati alla Visualizzazione. Utilizza anche hook o callback per aggiornare la Visualizzazione. Richiederà i dati dal Modello.

Il seguente flusso illustra il Model-View-ViewModel (MVVM) Pattern core. Come si differenzia questo da MVP?

  • Il ViewModel sostituisce il Presenter nello strato intermedio.
  • Il Presenter mantiene i riferimenti alla View. Il ViewModel no.
  • Il Presenter aggiorna la View utilizzando il metodo classico (attivando metodi).
  • Il ViewModel invia flussi di dati.
  • Il Presenter e la View sono in una relazione 1 a 1.
  • La View e il ViewModel sono in una relazione 1 a molti.
  • Il ViewModel non sa che la View sta ascoltando.

Ci sono due modi per implementare MVVM in Android:

  • Binding dei dati
  • RXJava

In questo tutorial, utilizzeremo solo il Data Binding. La Data Binding Library è stata introdotta da Google per legare i dati direttamente nel layout xml. Per ulteriori informazioni sul Data Binding, fare riferimento a questo tutorial. Creeremo un’applicazione di esempio di una semplice pagina di accesso che richiede input dall’utente. Vedremo come il ViewModel notifica la View quando mostrare un messaggio di Toast senza mantenere un riferimento alla View.

Come è possibile notificare una classe senza avere un riferimento ad essa? Può essere fatto in tre modi diversi:

  • Utilizzando il Binding dei Dati a due vie
  • Utilizzando i Live Data
  • Utilizzando RxJava

Binding dei dati bidirezionale

Il binding dei dati bidirezionale è una tecnica di collegamento degli oggetti ai layout XML in modo che l’oggetto e il layout possano inviarsi reciprocamente dati. Nel nostro caso, il ViewModel può inviare dati al layout e osservare anche le modifiche. Per fare ciò, abbiamo bisogno di un BindingAdapter e di un attributo personalizzato definito nell’XML. Il Binding Adapter sarà in ascolto delle modifiche nella proprietà dell’attributo. Approfondiremo ulteriormente il binding bidirezionale dei dati attraverso l’esempio seguente.

Struttura del Progetto di Esempio Android MVVM

Aggiunta della Libreria di Data Binding

Aggiungi il seguente codice al file build.gradle della tua app:

android {

    dataBinding {
        enabled = true
    }
}

Questo abilita il Data Binding nella tua applicazione.

Aggiunta delle Dipendenze

Aggiungi le seguenti dipendenze nel tuo file build.gradle :

implementation 'android.arch.lifecycle:extensions:1.1.0'

Model

Il Modello conterrà l’email e la password dell’utente. La seguente classe User.java lo fa:

package com.journaldev.androidmvvmbasics.model;


public class User {
    private String email;
    private String password;

    public User(String email, String password) {
        this.email = email;
        this.password = password;
    }

    public void setEmail(String email) {
        this.email = email;
    }


    public String getEmail() {
        return email;
    }

    public void setPassword(String password) {
        this.password = password;
    }


    public String getPassword() {
        return password;
    }


}

Il Two-way Data Binding ci permette di legare oggetti nei layout XML in modo che l’oggetto possa inviare dati al layout, e viceversa. La sintassi per il two-way data binding è @={variabile}

Layout

Il codice per l’activity_main.xml è riportato di seguito:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:bind="https://schemas.android.com/tools">

    <data>

        <variable
            name="viewModel"
            type="com.journaldev.androidmvvmbasics.viewmodels.LoginViewModel" />
    </data>


    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="8dp"
            android:orientation="vertical">

            <EditText
                android:id="@+id/inEmail"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Email"
                android:inputType="textEmailAddress"
                android:padding="8dp"
                android:text="@={viewModel.userEmail}" />


            <EditText
                android:id="@+id/inPassword"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Password"
                android:inputType="textPassword"
                android:padding="8dp"
                android:text="@={viewModel.userPassword}" />


            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:onClick="@{()-> viewModel.onLoginClicked()}"
                android:text="LOGIN"
                bind:toastMessage="@{viewModel.toastMessage}" />


        </LinearLayout>

    </ScrollView>

</layout>

Il Data Binding richiede di impostare il tag layout in alto. Qui il nostro ViewModel lega i dati alla View. ()-> viewModel.onLoginClicked() invoca il listener del clic del pulsante lambda definito nel nostro ViewModel. L’EditText aggiorna i valori nel Modello (tramite ViewModel). bind:toastMessage="@{viewModel.toastMessage}" è un attributo personalizzato che abbiamo creato per il two-way data binding. In base alle modifiche nel toastMessage nel ViewModel, il BindingAdapter verrà attivato nella View.

ViewModel

Il codice per il LoginViewModel.java è riportato di seguito:

package com.journaldev.androidmvvmbasics.viewmodels;

import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.text.TextUtils;
import android.util.Patterns;

import com.android.databinding.library.baseAdapters.BR;
import com.journaldev.androidmvvmbasics.model.User;

public class LoginViewModel extends BaseObservable {
    private User user;


    private String successMessage = "Login was successful";
    private String errorMessage = "Email or Password not valid";

    @Bindable
    private String toastMessage = null;


    public String getToastMessage() {
        return toastMessage;
    }


    private void setToastMessage(String toastMessage) {

        this.toastMessage = toastMessage;
        notifyPropertyChanged(BR.toastMessage);
    }


    public void setUserEmail(String email) {
        user.setEmail(email);
        notifyPropertyChanged(BR.userEmail);
    }

    @Bindable
    public String getUserEmail() {
        return user.getEmail();
    }

    @Bindable
    public String getUserPassword() {
        return user.getPassword();
    }

    public void setUserPassword(String password) {
        user.setPassword(password);
        notifyPropertyChanged(BR.userPassword);
    }

    public LoginViewModel() {
        user = new User("","");
    }

    public void onLoginClicked() {
        if (isInputDataValid())
            setToastMessage(successMessage);
        else
            setToastMessage(errorMessage);
    }

    public boolean isInputDataValid() {
        return !TextUtils.isEmpty(getUserEmail()) && Patterns.EMAIL_ADDRESS.matcher(getUserEmail()).matches() && getUserPassword().length() > 5;
    }
}

I metodi chiamati nel layout sono implementati nel codice sopra con la stessa firma. Se il controparte XML del metodo non esiste, dobbiamo cambiare l’attributo in app:. La classe sopra può anche estendere ViewModel. Ma abbiamo bisogno di BaseObservable poiché converte i dati in flussi e notifica quando la proprietà toastMessage viene cambiata. Dobbiamo definire il getter e il setter per l’attributo personalizzato toastMessage definito nel XML. All’interno del setter, notifichiamo l’osservatore (che sarà la View nella nostra applicazione) che i dati sono cambiati. La View (la nostra attività) può definire l’azione appropriata.

La classe BR è generata automaticamente dal data binding quando si ricostruisce il progetto

Il codice per la classe MainActivity.java è riportato di seguito:

package com.journaldev.androidmvvmbasics.views;


import android.databinding.BindingAdapter;
import android.databinding.DataBindingUtil;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;


import com.journaldev.androidmvvmbasics.R;
import com.journaldev.androidmvvmbasics.databinding.ActivityMainBinding;
import com.journaldev.androidmvvmbasics.viewmodels.LoginViewModel;


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        activityMainBinding.setViewModel(new LoginViewModel());
        activityMainBinding.executePendingBindings();

    }

    @BindingAdapter({"toastMessage"})
    public static void runMe(View view, String message) {
        if (message != null)
            Toast.makeText(view.getContext(), message, Toast.LENGTH_SHORT).show();
    }
}

Grazie al DataBinding, la classe ActivityMainBinding è generata automaticamente dal layout. Il metodo @BindingAdapter viene attivato ogni volta che l’attributo toastMessage definito sul pulsante viene cambiato. Deve utilizzare lo stesso attributo definito nel XML e nel ViewModel. Quindi, nell’applicazione sopra, il ViewModel aggiorna il Model ascoltando le modifiche nella View. Inoltre, il Model può aggiornare la view tramite il ViewModel utilizzando notifyPropertyChanged. L’output dell’applicazione sopra in azione è riportato di seguito: Questo conclude questo tutorial su Android MVVM utilizzando DataBinding. È possibile scaricare il progetto dal link fornito di seguito.

AndroidMVVMBasics

Link al progetto Github

Source:
https://www.digitalocean.com/community/tutorials/android-mvvm-design-pattern