In deze tutorial zullen we de Android MVVM-architectuurpatroon bespreken en implementeren in onze Android-applicatie. We hebben eerder het Android MVP-patroon besproken.
Waarom hebben we deze patronen nodig? Alles toevoegen in een enkele activiteit of fragment zou leiden tot problemen bij het testen en refactoren van de code. Daarom wordt het gebruik van scheiding van code en schone architectuur aanbevolen.
Android MVVM
MVVM staat voor Model, View, ViewModel.
- Model: Dit bevat de gegevens van de applicatie. Het kan niet rechtstreeks met de View praten. Over het algemeen wordt aanbevolen om de gegevens aan de ViewModel bloot te stellen via Observables.
- View: Het vertegenwoordigt de UI van de applicatie zonder enige toepassingslogica. Het observeert de ViewModel.
- ViewModel: Het fungeert als een schakel tussen het Model en de View. Het is verantwoordelijk voor het transformeren van de gegevens vanuit het Model. Het biedt gegevensstromen aan de View. Het gebruikt ook hooks of terugroepen om de View bij te werken. Het zal om de gegevens vragen vanuit het Model.
De volgende flow illustreert het kernpatroon van MVVM. Hoe verschilt dit van MVP?
- De ViewModel vervangt de Presenter in de Middellaag.
- De Presenter heeft verwijzingen naar de View. De ViewModel niet.
- De Presenter werkt de View bij met de klassieke methode (methoden activeren).
- De ViewModel stuurt gegevensstromen.
- De Presenter en de View hebben een 1-op-1-relatie.
- De View en de ViewModel hebben een 1-op-veel-relatie.
- De ViewModel weet niet dat de View naar hem luistert.
Er zijn twee manieren om MVVM in Android te implementeren:
- Data Binding
- RXJava
In deze tutorial zullen we alleen Data Binding gebruiken. De Data Binding-bibliotheek werd geïntroduceerd door Google om gegevens rechtstreeks te binden in de xml-indeling. Voor meer informatie over Data Binding, zie deze tutorial. We zullen een eenvoudige toepassing voor een inlogpagina maken die gebruikersinvoer vraagt. We zullen zien hoe de ViewModel de View op de hoogte stelt wanneer een Toast-bericht moet worden weergegeven zonder een verwijzing naar de View te behouden.
Hoe is het mogelijk om een klasse op de hoogte te stellen zonder er een verwijzing naar te hebben? Dit kan op drie verschillende manieren worden gedaan:
- Gebruik van Two Way Data Binding
- Gebruik van Live Data
- Gebruik van RxJava
Tweewegs gegevensbinding
Tweewegs gegevensbinding is een techniek om je objecten te binden aan je XML-indelingen, zodat het object en de indeling beide gegevens naar elkaar kunnen verzenden. In ons geval kan de ViewModel gegevens naar de indeling verzenden en ook wijzigingen observeren. Hiervoor hebben we een BindingAdapter
en aangepast attribuut gedefinieerd in de XML nodig. De Binding Adapter luistert naar wijzigingen in de attribuut-eigenschap. We zullen meer leren over tweewegs gegevensbinding via ons voorbeeld hieronder.
Voorbeeldprojectstructuur van Android MVVM
Het toevoegen van de Data Binding-bibliotheek
Voeg de volgende code toe aan het build.gradle-bestand van je app:
android {
dataBinding {
enabled = true
}
}
Dit activeert gegevensbinding in je applicatie.
Het toevoegen van de afhankelijkheden
Voeg de volgende afhankelijkheden toe aan je build.gradle
bestand:
implementation 'android.arch.lifecycle:extensions:1.1.0'
Model
Het Model zou de e-mail en het wachtwoord van de gebruiker bevatten. De volgende User.java klasse doet dat:
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;
}
}
Tweewegsgegevensbinding stelt ons in staat objecten te binden in de XML-indelingen, zodat het object gegevens naar de indeling kan sturen, en vice versa. De syntaxis voor tweewegsgegevensbinding is @={variabele}
Indeling
De code voor de activity_main.xml is hieronder gegeven:
<?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>
Databinding vereist dat we de layout-tag bovenaan instellen. Hier bindt onze ViewModel de gegevens aan de View. ()-> viewModel.onLoginClicked()
roept de knopklik-luisteraar lambda aan die is gedefinieerd in onze ViewModel. De EditText bijwerken de waarden in het Model (via ViewModel). bind:toastMessage="@{viewModel.toastMessage}"
is een aangepast attribuut dat we hebben gemaakt voor tweewegsgegevensbinding. Op basis van wijzigingen in toastMessage in de ViewModel wordt de BindingAdapter getriggerd in de View.
ViewModel
De code voor de LoginViewModel.java is hieronder gegeven:
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;
}
}
De methoden die in de lay-out worden aangeroepen, zijn geïmplementeerd in de bovenstaande code met dezelfde handtekening. Als de XML-tegenhanger van de methode niet bestaat, moeten we het attribuut wijzigen naar app:
. De bovenstaande klasse kan ook ViewModel uitbreiden. Maar we hebben BaseObservable nodig omdat het de gegevens omzet in streams en meldt wanneer de toastMessage
-eigenschap is gewijzigd. We moeten de getter en setter definiëren voor het toastMessage-aangepaste attribuut dat in de XML is gedefinieerd. Binnen de setter melden we de waarnemer (die de View zal zijn in onze toepassing) dat de gegevens zijn gewijzigd. De View (Onze activiteit) kan de juiste actie definiëren.
BR-klasse wordt automatisch gegenereerd door gegevensbinding wanneer u het project opnieuw opbouwt
De code voor de klasse MainActivity.java
wordt hieronder gegeven:
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();
}
}
Dankzij DataBinding wordt de klasse ActivityMainBinding
automatisch gegenereerd uit de lay-out. De @BindingAdapter
-methode wordt geactiveerd wanneer het toastMessage-attribuut dat op de knop is gedefinieerd, wordt gewijzigd. Het moet hetzelfde attribuut gebruiken als gedefinieerd in de XML en in de ViewModel. Dus in de bovenstaande toepassing werkt de ViewModel de Model bij door te luisteren naar de wijzigingen in de View. Ook kan het Model de weergave bijwerken via de ViewModel met behulp van de notifyPropertyChanged
De uitvoer van de bovenstaande toepassing in actie wordt hieronder gegeven: Hiermee komt er een einde aan deze zelfstudie over Android MVVM met behulp van DataBinding. U kunt het project downloaden via de onderstaande link.
Source:
https://www.digitalocean.com/community/tutorials/android-mvvm-design-pattern