Android MVVM: Benutzeroberflächen dynamisch mit LiveData und Data Binding aktualisieren
In diesem Beitrag werden wir die MVVM-Architektur in Android verwenden und dabei LiveData und Data Binding kombinieren. LiveData erleichtert es, die Benutzeroberfläche (UI) automatisch zu aktualisieren, sobald sich Daten im ViewModel ändern. Wir zeigen dir, wie diese Technologien in einer einfachen Login-Anwendung zusammenarbeiten.
Einleitung
MVVM (Model-View-ViewModel) ist ein Architekturpattern, das in der Android-Entwicklung häufig genutzt wird, um eine saubere Trennung der Schichten in einer Anwendung zu gewährleisten. Dabei wird das Model für die Datenlogik und das ViewModel als Vermittler zwischen Model und View genutzt. Data Binding und LiveData machen das Arbeiten mit MVVM besonders effizient, da sie den Code vereinfachen und Fehlerquellen minimieren.
Was ist LiveData?
LiveData ist ein Container, der Daten hält und Änderungen daran beobachtet. Das Besondere an LiveData ist seine lebenszyklusbewusste Natur: Es wird nur dann aktualisiert, wenn die UI im Vordergrund aktiv ist. Dies reduziert Abstürze erheblich, da Updates an die UI nicht mehr ausgeführt werden, wenn die Activity oder das Fragment bereits zerstört wurde.
Projektstruktur und Abhängigkeiten
Um mit MVVM und LiveData zu starten, müssen wir zunächst die entsprechenden Abhängigkeiten in die build.gradle
-Datei unseres Projekts hinzufügen:
android {
...
dataBinding {
enabled = true
}
...
}
dependencies {
...
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'com.android.support:design:28.0.0-beta01'
...
}
Diese Abhängigkeiten ermöglichen es uns, Data Binding und LiveData zu nutzen.
Model-Schicht
Das Model wird durch die User
-Klasse dargestellt. Diese enthält die Validierungslogik für die Eingabefelder:
public class User {
private String mEmail;
private String mPassword;
public User(String email, String password) {
mEmail = email;
mPassword = password;
}
public String getEmail() {
return mEmail == null ? "" : mEmail;
}
public String getPassword() {
return mPassword == null ? "" : mPassword;
}
public boolean isEmailValid() {
return Patterns.EMAIL_ADDRESS.matcher(getEmail()).matches();
}
public boolean isPasswordLengthGreaterThan5() {
return getPassword().length() > 5;
}
}
Layout mit Data Binding
Die XML-Datei activity_main.xml
stellt die Benutzeroberfläche bereit und bindet das ViewModel:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="loginViewModel"
type="com.example.androidmvvm.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.support.design.widget.TextInputLayout android:layout_width="match_parent">
<EditText android:id="@+id/inEmail" android:layout_width="match_parent"
android:hint="Email" android:text="@={loginViewModel.email}" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout android:layout_width="match_parent">
<EditText android:id="@+id/inPassword" android:layout_width="match_parent"
android:hint="Passwort" android:text="@={loginViewModel.password}" />
</android.support.design.widget.TextInputLayout>
<Button android:id="@+id/button" android:layout_width="match_parent"
android:text="LOGIN" android:onClick="@{()-> loginViewModel.onLoginClicked()}" />
<ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyleLarge"
android:visibility="@{loginViewModel.busy}" />
</LinearLayout>
</ScrollView>
</layout>
ViewModel
Im ViewModel implementieren wir die Logik, die mit dem UI-Thread interagiert:
public class LoginViewModel extends ViewModel {
public MutableLiveData<String> email = new MutableLiveData<>();
public MutableLiveData<String> password = new MutableLiveData<>();
public MutableLiveData<String> errorEmail = new MutableLiveData<>();
public MutableLiveData<String> errorPassword = new MutableLiveData<>();
private MutableLiveData<User> userMutableLiveData = new MutableLiveData<>();
public LiveData<User> getUser() {
return userMutableLiveData;
}
public void onLoginClicked() {
// Setze die ProgressBar auf sichtbar
new Handler().postDelayed(() -> {
User user = new User(email.getValue(), password.getValue());
if (!user.isEmailValid()) {
errorEmail.setValue("Bitte eine gültige E-Mail eingeben");
}
if (!user.isPasswordLengthGreaterThan5()) {
errorPassword.setValue("Das Passwort muss länger als 5 Zeichen sein");
}
userMutableLiveData.setValue(user);
}, 3000);
}
}
MainActivity
In der MainActivity beobachten wir die Änderungen am LiveData:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
LoginViewModel loginViewModel = ViewModelProviders.of(this).get(LoginViewModel.class);
binding.setLoginViewModel(loginViewModel);
binding.setLifecycleOwner(this);
loginViewModel.getUser().observe(this, user -> {
if (user != null) {
Toast.makeText(this, "E-Mail: " + user.getEmail(), Toast.LENGTH_SHORT).show();
}
});
}
}
Zusammenfassung
In diesem Beitrag haben wir gezeigt, wie MVVM in Android mit LiveData und Data Binding implementiert werden kann. Durch die Verwendung dieser Technologien wird die Entwicklung einfacher und fehlerfreier, da der Code klar strukturiert und gut wartbar ist.