Kotlin Reactive Programming für einen Android-Anmeldebildschirm

RxJava 2.0 ist eine beliebte reaktive Programmierbibliothek, die unzähligen Android-Entwicklern dabei geholfen hat, schnell reagierende Apps mit weniger Code und geringerer Komplexität zu erstellen, insbesondere wenn es darum geht, mehrere Threads zu verwalten.

Wenn Sie zu den vielen Entwicklern gehören, die zu Kotlin gewechselt haben, heißt das nicht, dass Sie RxJava aufgeben müssen! 

Im ersten Teil dieser Serie habe ich Ihnen gezeigt, wie Sie von der Programmierung mit RxJava 2.0 in Java zur Programmierung mit RxJava in wechseln Kotlin. Außerdem haben wir untersucht, wie Sie Boilerplate aus Ihren Projekten verbannen können, indem Sie die Erweiterungsfunktionen von RxKotlin nutzen. Außerdem erfahren Sie, wie Sie das SAM-Konvertierungsproblem umgehen können, dem viele Entwickler beim ersten Einsatz von RxJava 2.0 mit Kotlin begegnen. 

In diesem zweiten Teil konzentrieren wir uns darauf, wie RxJava helfen kann, die Probleme zu lösen, die bei echten Android-Projekten auftreten, indem wir eine reaktive Android-Anwendung mit RxJava 2.0, RxAndroid und RxBinding erstellen.

Wie kann ich RxJava in realistischen Projekten verwenden??

In unserem Artikel über reaktive Programmierung mit RxJava und RxKotlin haben wir einige einfache Funktionen erstellt Observables und Beobachter die Druckdaten auf Android Studio Logcat-Aber so werden Sie RxJava in der realen Welt nicht verwenden.

In diesem Artikel werde ich Ihnen zeigen, wie Sie mit RxJava einen Bildschirm erstellen, der in unzähligen Android-Anwendungen verwendet wird: dem Klassiker Anmelden Bildschirm. 

Wenn Ihre App hat irgendein Art der Anmeldeerfahrung, dann hat es normalerweise strenge Regeln für die Art der Informationen, die es akzeptiert. Beispielsweise muss das Kennwort möglicherweise eine bestimmte Anzahl von Zeichen überschreiten oder die E-Mail-Adresse muss ein gültiges E-Mail-Format haben.

Während du könnte Überprüfen Sie die Eingaben des Benutzers, sobald Sie auf die Schaltfläche klicken Anmelden Diese Schaltfläche ist nicht die beste Benutzererfahrung, da sie Informationen übermitteln kann, die von Ihrer Anwendung offensichtlich nicht akzeptiert werden.

Es ist weitaus besser, den Benutzer beim Tippen zu überwachen und ihm dann ein Heads-up zu geben, sobald klar wird, dass er Informationen eingibt, die den Anforderungen Ihrer App nicht entsprechen. Durch diese Art von Live-Feedback und laufendem Feedback geben Sie dem Benutzer die Möglichkeit, seine Fehler zu korrigieren Vor das zu schlagen Anmelden Taste.

Während du könnte Überwachen Sie die Benutzeraktivität mit Vanilla Kotlin. Wir können diese Funktionalität mit viel weniger Code bereitstellen, indem Sie die Hilfe von RxJava sowie einige andere verwandte Bibliotheken verwenden.

Erstellen der Benutzeroberfläche

Beginnen wir mit dem Aufbau unserer Benutzeroberfläche. Ich werde folgendes hinzufügen:

  • Zwei EditTexts, wo der Benutzer seine E-Mail-Adresse eingeben kann (Email eingeben) und Passwort (Passwort eingeben).
  • Zwei TextInputLayout Verpackungen, die unsere umgeben werden Email eingeben und Passwort eingeben EditTexts. Diese Wrapper zeigen eine Warnung an, wenn der Benutzer eine E-Mail-Adresse oder ein Kennwort eingibt, die nicht den Anforderungen unserer App entsprechen.
  • Eine Schaltfläche zur Sichtbarkeit von Kennwörtern, mit der der Benutzer zwischen der Maskierung des Kennworts und der Anzeige als unformatierter Text wechseln kann.
  • EIN Anmelden Taste. Damit dieses Beispiel weiterhin auf RxJava ausgerichtet ist, werde ich diesen Teil der Anmeldeerfahrung nicht implementieren. Daher werde ich diese Schaltfläche als deaktiviert markieren.

Hier ist mein fertiges Layout:

        

Sie können dies kopieren und in Ihre App einfügen, wenn Sie möchten, oder Sie können den Quellcode des Projekts von unserem GitHub-Repo herunterladen.

Erstellen einer reaktiven Anmeldeerfahrung mit Kotlin

Sehen wir uns nun an, wie wir RxJava und einige zugehörige Bibliotheken verwenden können, um Benutzereingaben zu überwachen und in Echtzeit Feedback zu geben. 

Ich werde das anpacken Anmelden Bildschirm in zwei Teilen. Im ersten Abschnitt zeige ich Ihnen, wie Sie die RxBinding-Bibliothek zum Registrieren und Reagieren auf Textänderungsereignisse verwenden. Im zweiten Abschnitt erstellen wir einige Transformationsfunktionen, die die Eingaben des Benutzers überprüfen und gegebenenfalls eine Fehlermeldung anzeigen.

Erstellen Sie ein neues Projekt mit den Einstellungen Ihrer Wahl, aber wenn Sie dazu aufgefordert werden, stellen Sie sicher, dass Sie die Option auswählen Kotlin-Support einbeziehen Kontrollkästchen. 

Antworten auf Textänderungsereignisse

In diesem Abschnitt implementieren wir die folgende Funktionalität: 

  • Erkennen Sie, wann der Benutzer den Code eingibt Email eingeben Feld.
  • Ignorieren Sie alle Textänderungsereignisse, die innerhalb kurzer Zeit auftreten, da dies darauf hinweist, dass der Benutzer noch tippt. 
  • Führen Sie eine Aktion aus, wenn der Benutzer die Eingabe abbricht. In unserer fertigen App überprüfen wir hier die Eingaben des Benutzers. In diesem Abschnitt wird jedoch nur eine angezeigt Toast

1. RxBinding 

RxBinding ist eine Bibliothek, die das Konvertieren einer großen Anzahl von UI-Ereignissen in Observables vereinfacht. Sie können sie dann wie jeden anderen RxJava-Datenstrom behandeln.

Wir werden Textänderungsereignisse überwachen, indem wir die von RxBinding kombinieren widget.RxTextView mit dem afterTextChangeEvents Methode zum Beispiel:

RxTextView.afterTextChangeEvents (enterEmail)

Das Problem bei der Behandlung von Textänderungsereignissen als Datenströme ist, dass zunächst beide Email eingeben und enterPassword EditTexts wird leer sein, und wir möchten nicht, dass unsere App auf diesen leeren Status reagiert, als wäre dies die erste Datenemission im Stream. RxBinding löst dieses Problem, indem Sie a skipInitialValue () Diese Methode wird verwendet, um jeden Observer anzuweisen, den ursprünglichen Wert des Streams zu ignorieren.

 RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue ()

Ich sehe mir die RxBinding-Bibliothek in meinem Artikel zu RxJava 2 für Android-Apps genauer an.

2. RxJava .debounce () Operator

Um die bestmögliche Benutzererfahrung zu bieten, müssen wir alle relevanten Kennwort- oder E-Mail-Warnungen anzeigen, nachdem der Benutzer die Eingabe abgeschlossen hat, jedoch bevor der Benutzer auf die Schaltfläche "." Anmelden Taste.

Ohne RxJava müssten wir normalerweise ein a implementieren, um dieses enge Zeitfenster zu identifizieren Timer, aber in RxJava müssen wir nur die debounce () Betreiber zu unserem Datenstrom.

Ich werde das verwenden debounce () Operator, um alle Textänderungsereignisse herauszufiltern, die in schneller Folge auftreten, d. h. wenn der Benutzer noch tippt. Hier ignorieren wir alle Textänderungsereignisse, die innerhalb desselben 400-Millisekunden-Fensters stattfinden:

 RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS)

3. RxAndroid's AndroidSchedulers.mainThread ()

Die RxAndroid-Bibliothek AndroidSchedulers.mainThread bietet uns eine einfache Möglichkeit, zum wichtigsten UI-Thread von Android zu wechseln.

Da es nur möglich ist, die Android-Benutzeroberfläche über den Haupt-UI-Thread zu aktualisieren, müssen wir sicherstellen, dass wir uns in diesem Thread befinden, bevor wir versuchen, E-Mail- oder Kennwortwarnungen anzuzeigen, und bevor wir unsere anzeigen Toast

 RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ())

4. Abonnieren

Um die Daten zu empfangen, die von gesendet werden Email eingeben, wir müssen es abonnieren:

 RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .subscribe 

5. Zeigen Sie den Toast an

Schließlich möchten wir, dass unsere Anwendung auf Textänderungsereignisse reagiert, indem sie die Eingaben des Benutzers überprüft. Um die Dinge jedoch einfacher zu halten, werde ich an dieser Stelle einfach ein a anzeigen Toast

Ihr Code sollte ungefähr so ​​aussehen:

import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.widget.Toast import com.jakewharton.rxbinding2.widget.RxTextView import kotlinx.android.synthetic.main.activity_main. * importieren .schadulatoren.AndroidSchedulers importieren die java.util.concurrent.TimeUnit-Klasse MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) RachTailaday (). skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .subscribe Toast.makeText (dies "400 Millisekunden seit letzter Textänderung", Toast.LENGTH_SHORT).

6. Aktualisieren Sie Ihre Abhängigkeiten

Da wir ein paar verschiedene Bibliotheken verwenden, müssen wir unsere Projekte öffnen build.gradle Datei und fügen Sie RxJava, RxBinding und RxAndroid als Projektabhängigkeiten hinzu: 

Abhängigkeiten Implementierungsdateibaum (Verzeichnis: 'libs', include: ['* .jar']) Implementierung "org.jetbrains.kotlin: kotlin-stdlib-jdk7: $ kotlin_version" Implementierung "com.android.support:design:28.0. 0-alpha1 'Implementierung' com.android.support:appcompat-v7:28.0.0-alpha1 'Implementierung' com.android.support.constraint: constraint-layout: 1.1.0 '// Hinzufügen der RxJava-Abhängigkeit // Implementierung' io.reactivex.rxjava2: rxjava: 2.1.9 '// Hinzufügen der RxAndroid-Abhängigkeit // Implementierung' io.reactivex.rxjava2: rxandroid: 2.0.2 '// Hinzufügen der RxBinding-Abhängigkeit // Implementierung' com.jakewharton.rxbinding2: rxbinding: 2.1.1 '

Sie können diesen Teil Ihres Projekts testen, indem Sie ihn auf Ihrem physischen Android-Smartphone oder -Tablet oder auf dem Android Virtual Device (AVD) installieren. Wähle aus Email eingeben Text bearbeiten und anfangen zu tippen; ein Toast sollte angezeigt werden, wenn Sie mit dem Tippen aufhören. 

Überprüfung der Benutzereingaben mit Transformationsfunktionen

Als Nächstes müssen wir einige Grundregeln für die Art der Eingabe festlegen, die unsere Anwendung akzeptiert, und dann die Eingabe des Benutzers anhand dieser Kriterien prüfen und gegebenenfalls eine Fehlermeldung anzeigen. 

Die Überprüfung der E-Mail-Adresse oder des Passworts des Benutzers ist ein mehrstufiger Prozess. Um den Code lesbarer zu machen, kombiniere ich alle diese Schritte in ihre eigenen Transformationsfunktion. 

Hier ist der Beginn des E-Mail validieren Transformationsfunktion:

// Definiere einen ObservableTransformer. Eingabe und Ausgabe müssen eine Zeichenfolge sein, // private val validateEmailAddress = ObservableTransformer observable -> // Verwenden Sie flatMap, um eine Funktion auf jedes Element anzuwenden, das von der Observable // observable.flatMap ausgegeben wird. // // Trennen Sie alle Leerzeichen am Anfang und Ende der Benutzereingabe. // Observable.just (it) .map  it.trim () // Überprüfen Sie, ob die Eingabe mit dem E-Mail-Muster von Android übereinstimmt // .filter Patterns.EMAIL_ADDRESS.matcher (it) .matches ()

Im obigen Code verwenden wir die Filter() Operator, um die Ausgabe des Observable danach zu filtern, ob es mit dem von Android übereinstimmt Patterns.EMAIL_ADDRESS Muster.

Im nächsten Teil der Transformationsfunktion müssen wir angeben, was passiert, wenn die Eingabe nicht mit dem übereinstimmt E-MAIL-ADDRESSE Muster. Standardmäßig löst jeder nicht behebbare Fehler einen Aufruf an aus onError (), was den Datenstrom beendet. Anstatt den Stream zu beenden, möchten wir, dass unsere Anwendung eine Fehlermeldung anzeigt, also werde ich sie verwenden onErrorResumeNext, Dadurch wird das Observable angewiesen, auf einen Fehler zu reagieren, indem die Kontrolle an ein neues Observable übergeben wird, anstatt aufzurufen onError (). Dadurch können wir unsere benutzerdefinierte Fehlermeldung anzeigen.

// Wenn die Eingabe des Benutzers nicht mit dem E-Mail-Muster übereinstimmt, wird ein Fehler ausgegeben. // .singleOrError () .onErrorResumeNext if (NoSuchElementException) Single.error (Ausnahme ("Bitte geben Sie eine gültige E-Mail-Adresse ein"))  else Single.error (it) .toObservable ()

Der abschließende Schritt besteht darin, diese Transformationsfunktion mithilfe des Befehls auf den E-Mail-Datenstrom anzuwenden .komponieren() Operator. An diesem Punkt dein MainActivity.kt sollte ungefähr so ​​aussehen: 

import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Patterns importieren io.reactivex.Observable import io.reactivex.ObservableTransformer importieren io.reactivex.Single importieren io.reactivex.android.schedulers.AndroidSulated import kotlinx.android.synthetic.main.activity_main. * import java.util.concurrent.TimeUnit import com.jakewharton.rxbinding2.widget.RxTextView-Klasse MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState:) (savedInstanceState) setContentView (R.layout.activity_main) RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .map emailError.error = null it.view (). text.toString () .debounce (400) // Wir befinden uns im Android-UI-Thread // TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .compose (validateEmailAddress) .compose (retryWhenError passwordError.error = it.message) .subscribe () // if Die App stößt auf einen Fehler. Versuchen Sie es dann erneut mit // private Inline-Funktion n retryWhenError (crossinline onError: (ex: Throwable) -> Unit): ObservableTransformer = ObservableTransformer observable -> observable.retryWhen errors -> // Verwenden Sie den flatmap () - Operator, um alle Emissionen in einem einzigen Observable-Element zu reduzieren. // errors.flatMap onError (it) Observable.just ("") / / Definiere einen ObservableTransformer, wo wir die E-Mail-Validierung durchführen // private val validateEmailAddress = ObservableTransformer observable -> observable.flatMap Observable.just (it) .map it.trim () // Prüfen Sie, ob die Benutzereingabe mit dem E-Mail-Muster von Android übereinstimmt // .filter Patterns.EMAIL_ADDRESS.matcher (it) .matches ( ) // Wenn die Eingabe des Benutzers nicht mit dem E-Mail-Muster übereinstimmt, wird eine Fehlermeldung ausgegeben. // .singleOrError () .onErrorResumeNext if (NoSuchElementException) Single.error (Ausnahme ("Bitte geben Sie eine gültige E-Mail-Adresse ein.") )) else Single.error (it) .toObservable ()

Installieren Sie dieses Projekt auf Ihrem Android-Gerät oder AVD, und Sie werden feststellen, dass der E-Mail-Teil der Anmelden Der Bildschirm überprüft nun Ihre Eingabe erfolgreich. Versuchen Sie, etwas anderes als eine E-Mail-Adresse einzugeben. Die App wird Sie darauf hinweisen, dass dies keine gültige Eingabe ist. 

Spülen und wiederholen: Überprüfen des Benutzerpassworts

An diesem Punkt haben wir ein voll funktionierendes Email eingeben Feld- und Umsetzung Passwort eingeben ist meistens nur ein Fall der Wiederholung der gleichen Schritte.

In der Tat ist der einzige große Unterschied, dass unser validatePassword Die Transformationsfunktion muss nach verschiedenen Kriterien suchen. Ich werde angeben, dass das Passwort des Benutzers mindestens 7 Zeichen lang sein muss: 

 .filter it.length> 7

Nachdem alle vorherigen Schritte wiederholt wurden, ist der Vorgang abgeschlossen MainActivity.kt sollte ungefähr so ​​aussehen: 

import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Patterns importieren io.reactivex.Observable import io.reactivex.ObservableTransformer importieren io.reactivex.Single importieren io.reactivex.android.schedulers.AndroidSulated import kotlinx.android.synthetic.main.activity_main. * import java.util.concurrent.TimeUnit import com.jakewharton.rxbinding2.widget.RxTextView-Klasse MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState:) (savedInstanceState) setContentView (R.layout.activity_main) // Reagieren Sie auf Textänderungsereignisse in enterEmail // RxTextView.afterTextChangeEvents (enterEmail) // Überspringen Sie den initialen, leeren Zustand von enterEmail // .skipInitialValue () // Transformieren Sie die gesendeten Daten / / .map emailError.error = null // Konvertiere die Benutzereingabe in einen String // it.view (). text.toString () // // Ignoriere alle Emissionen, die innerhalb eines Zeitraums von 400 Millisekunden auftreten. // .debounce (400 // Vergewissere dich, dass wir uns im Android-UI-Thread // Tim befinden eUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) // Die Transformationsfunktion validateEmailAddress // .compose (validateEmailAddress) // anwenden Die Transformationsfunktion retryWhenError // .compose (retryWhenError itError.error itError.error) .subscribe () // Spülen und wiederholen Sie das enterPassword-EditText // RxTextView.afterTextChangeEvents (enterPassword) .skipInitialValue () .map passwordError.error = null it.view (). text.toString () .debounce (400) TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .compose (validatePassword) .compose (retryWhenError passwordError.error = it.message) .subscribe () // Wenn die App einen Fehler aufweist, versuchen Sie es erneut / / private Inline Fun retryWhenError (crossinline onError: (ex: Throwable) -> Unit): ObservableTransformer = ObservableTransformer observable -> observable.retryWhen errors -> /// Verwenden Sie den flatmap () - Operator, um alle Emissionen in eine einzige beobachtbare // errors.flatMap onError (it) Observable.just ("") zu reduzieren. // Definieren Sie unseren ObservableTransformer und geben Sie an, dass die Eingabe und Ausgabe eine Zeichenfolge sein muss. // private val validatePassword = ObservableTransformer observable -> observable.flatMap Observable.just (it) .map it.trim () // Erlauben Sie nur Kennwörter mit mindestens 7 Zeichen // .filter it.length> 7 // Wenn das Das Kennwort ist weniger als 7 Zeichen. Dann wird ein Fehler ausgegeben. // .singleOrError () // Wenn ein Fehler auftritt… // .onErrorResumeNext if (es handelt sich um NoSuchElementException) // Zeigt die folgende Meldung im KennwortError-TextInputLayout an // Single. error (Ausnahme ("Ihr Kennwort muss aus mindestens 7 Zeichen bestehen")) else Single.error (it) .toObservable () // Definieren Sie einen ObservableTransformer, in dem die E-Mail-Validierung // privat ausgeführt wird val validateEmailAddress = ObservableTransformer observable -> observable.flatMap Observable.just (it) .map it.trim () // Prüfen Sie, ob die Benutzereingabe mit dem E-Mail-Muster von Android übereinstimmt // .filter Patterns.EMAIL_ADDRESS.matcher (it) .matches ( ) // Wenn die Eingabe des Benutzers nicht mit dem E-Mail-Muster übereinstimmt… // .singleOrError () .onErrorResumeNext if (NoSuchElementException) //// Zeigt die folgende Nachricht in emailError an TextInputLayout // Single.error ( Ausnahme ("Bitte geben Sie eine gültige E-Mail-Adresse ein")) else Single.error (it) .toObservable ()

Installieren Sie dieses Projekt auf Ihrem Android-Gerät oder AVD und experimentieren Sie mit der Eingabe in der Email eingeben und Passwort eingeben Felder. Wenn Sie einen Wert eingeben, der den Anforderungen der App nicht entspricht, wird die entsprechende Warnmeldung angezeigt, ohne Sie müssen auf die Anmelden Taste.

Sie können dieses komplette Projekt von GitHub herunterladen.

Fazit

In diesem Artikel haben wir uns angesehen, wie RxJava helfen kann, die realen Probleme zu lösen, auf die Sie bei der Entwicklung Ihrer eigenen Android-Anwendungen stoßen, indem Sie RxJava 2.0, RxBinding und RxAndroid verwenden, um eine Anmelden Bildschirm. 

Weitere Hintergrundinformationen zur RxJava-Bibliothek finden Sie in unserem Artikel Erste Schritte mit RxJava 2.0.