Programmation réactive Kotlin pour un écran d'inscription Android

RxJava 2.0 est une bibliothèque de programmation réactive populaire qui a aidé d'innombrables développeurs Android à créer des applications hautement réactives, utilisant moins de code et moins de complexité., notamment quand il s'agit de gérer plusieurs threads.

Si vous faites partie des nombreux développeurs qui ont migré vers Kotlin, cela ne signifie pas pour autant que vous deviez abandonner RxJava.! 

Dans la première partie de cette série, je vous ai montré comment passer de la programmation avec RxJava 2.0 en Java à la programmation avec RxJava en Kotlin. Nous avons également examiné comment bannir les projets standard de vos projets en tirant parti des fonctions d'extension de RxKotlin, ainsi que le secret permettant d'éviter le problème de conversion SAM rencontré par de nombreux développeurs lorsqu'ils ont commencé à utiliser RxJava 2.0 avec Kotlin.. 

Dans cette deuxième partie, nous allons nous concentrer sur la façon dont RxJava peut vous aider à résoudre les problèmes que vous rencontrerez dans des projets Android réels, en créant une application Android réactive utilisant RxJava 2.0, RxAndroid et RxBinding..

Comment utiliser RxJava dans des projets réels?

Dans notre article sur la programmation réactive avec RxJava et RxKotlin, nous avons créé des Observables et Observateurs qui impriment des données sur Android Studio Logcat-mais ce n'est pas comme ça que vous utiliserez RxJava dans le monde réel.

Dans cet article, je vais vous montrer comment utiliser RxJava pour créer un écran utilisé dans d'innombrables applications Android: le classique S'inscrire écran. 

Si votre application a tout type d’expérience d’inscription, il est généralement soumis à des règles strictes concernant le type d’information accepté. Par exemple, le mot de passe doit peut-être dépasser un certain nombre de caractères ou l'adresse de messagerie doit être dans un format de courrier électronique valide..

Pendant que tu pourrait vérifier l'entrée de l'utilisateur une fois qu'ils ont frappé le S'inscrire bouton, ce n'est pas la meilleure expérience utilisateur, car cela les laisse ouverts à la soumission d'informations qui ne seront clairement jamais acceptées par votre application.

Il est de loin préférable de surveiller l'utilisateur au cours de la frappe, puis de l'avertir dès qu'il devient évident qu'il entre des informations qui ne répondent pas aux exigences de votre application. En fournissant ce type de commentaires en direct et continus, vous donnez à l'utilisateur la possibilité de corriger ses erreurs. avant frapper ça S'inscrire bouton.

Pendant que tu pourrait surveiller l’activité des utilisateurs avec Kotlin, nous pouvons fournir cette fonctionnalité en utilisant beaucoup moins de code en faisant appel à RxJava, ainsi qu’à quelques autres bibliothèques associées.

Création de l'interface utilisateur

Commençons par construire notre interface utilisateur. Je vais ajouter ce qui suit:

  • Deux EditTexts, où l'utilisateur peut entrer son adresse email (enterEmail) et mot de passe (Entrer le mot de passe).
  • Deux TextInputLayout emballages, qui entourera notre enterEmail et Entrer le mot de passe EditTexts. Ces wrappers afficheront un avertissement chaque fois que l'utilisateur entrera une adresse email ou un mot de passe qui ne répond pas aux exigences de notre application..
  • Un bouton de visibilité du mot de passe, qui permet à l'utilisateur de basculer entre le masquage du mot de passe et sa visualisation en texte brut.
  • UNE S'inscrire bouton. Pour que cet exemple reste concentré sur RxJava, je ne vais pas implémenter cette partie de l'expérience d'inscription, je vais donc marquer ce bouton comme étant désactivé..

Voici ma mise en page terminée:

        

Vous pouvez copier / coller ceci dans votre application si vous le souhaitez, ou vous pouvez simplement télécharger le code source du projet depuis notre dépôt GitHub..

Créer une expérience de connexion réactive avec Kotlin

Voyons maintenant comment utiliser RxJava, ainsi que quelques bibliothèques associées, pour surveiller les entrées des utilisateurs et fournir des commentaires en temps réel.. 

Je vais m'attaquer au S'inscrire écran en deux parties. Dans la première section, je vais vous montrer comment utiliser la bibliothèque RxBinding pour enregistrer et répondre aux événements de modification de texte. Dans la deuxième section, nous allons créer des fonctions de transformation qui valident l'entrée de l'utilisateur, puis afficherons un message d'erreur le cas échéant..

Créez un nouveau projet avec les paramètres de votre choix, mais lorsque vous y êtes invité, veillez à sélectionner le Inclure le support Kotlin case à cocher. 

Répondre aux événements de modification de texte

Dans cette section, nous allons implémenter les fonctionnalités suivantes: 

  • Détecter lorsque l'utilisateur tape dans le enterEmail champ.
  • Ignorer tous les événements de modification de texte survenant dans un court laps de temps, car cela indique que l'utilisateur est toujours en train de taper. 
  • Effectuer une action lorsque l'utilisateur cesse de taper. Dans notre application terminée, c’est là que nous allons valider la saisie de l’utilisateur, mais dans cette section, je vais simplement afficher un Pain grillé

1. RxBinding 

RxBinding est une bibliothèque qui facilite la conversion d'un grand nombre d'événements d'interface utilisateur en observables. Vous pouvez les traiter comme n'importe quel autre flux de données RxJava..

Nous allons surveiller les événements de changement de texte, en combinant les informations de RxBinding. widget.RxTextView avec le afterTextChangeEvents méthode, par exemple:

RxTextView.afterTextChangeEvents (enterEmail)

Le problème avec le traitement des événements de modification de texte en tant que flux de données est qu’au début, le enterEmail et enterPassword EditTexts sera vide, et nous ne voulons pas que notre application réagisse à cet état vide comme s'il s'agissait de la première émission de données du flux. RxBinding résout ce problème en fournissant un skipInitialValue () méthode que nous utiliserons pour demander à chaque observateur d'ignorer la valeur initiale de leur flux.

 RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue ()

Je regarde la bibliothèque RxBinding plus en détail dans mon article sur RxJava 2 pour Android Apps.

2. RxJava's .debounce () Opérateur

Pour offrir la meilleure expérience utilisateur possible, nous devons afficher tous les avertissements de mot de passe ou de courrier électronique pertinents une fois que l'utilisateur a fini de taper, mais avant de cliquer sur S'inscrire bouton.

Sans RxJava, l’identification de cette fenêtre temporelle nécessiterait généralement la mise en œuvre d’une Minuteur, mais dans RxJava nous avons juste besoin d'appliquer le debounce () opérateur à notre flux de données.

Je vais utiliser le debounce () opérateur pour filtrer tous les événements de modification de texte qui se produisent dans une succession rapide, c’est-à-dire lorsque l’utilisateur tape encore. Ici, nous ignorons tous les événements de modification de texte qui se produisent dans la même fenêtre de 400 millisecondes:

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

3. RxAndroid AndroidSchedulers.mainThread ()

La bibliothèque de RxAndroid AndroidSchedulers.mainThread nous donne un moyen facile de basculer vers le fil principal de l'interface utilisateur très important d'Android.

Dans la mesure où il est uniquement possible de mettre à jour l'interface utilisateur d'Android à partir du fil principal de l'interface utilisateur, nous devons nous assurer que nous sommes dans ce fil avant d'essayer d'afficher des avertissements d'e-mail ou de mot de passe, et avant d'afficher notre message. Pain grillé

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

4. Abonnez-vous

Pour recevoir les données émises par enterEmail, nous devons nous y abonner:

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

5. Afficher le pain grillé

Finalement, nous voulons que notre application réponde aux événements de modification de texte en validant la saisie de l'utilisateur, mais pour que les choses restent simples, je vais simplement afficher un Pain grillé

Votre code devrait ressembler à ceci:

importer android.support.v7.app.AppCompatActivity importer android.os.Bundle importer android.widget.Toast importer importer com.jakewharton.rxbinding2.widget.RxTextView importer kotlinx.android.synthetic.main.activity_main. * importer io.reactivex.android .schedulers.AndroidSchedulers import java.util.concurrent.TimeUnit classe MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState: Bundle?) super.onCréer (savedInstanceState) setContentView (R.layout.activity_Rony). skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .subscribe Toast.makeText (this, "400 millisecondes depuis le dernier changement de texte", Toast.LENGTH_SHORT) .show ()

6. Mettez à jour vos dépendances

Puisque nous utilisons plusieurs bibliothèques différentes, nous devons ouvrir le fichier de notre projet. build.gradle déposer et ajouter RxJava, RxBinding et RxAndroid en tant que dépendances du projet: 

dépendances implementation fileTree (dir: 'libs', include: ['* .jar'])) implémentation "org.jetbrains.kotlin: kotlin-stdlib-jdk7: $ kotlin_version" implémentation "com.android.support:design:28.0. 0-alpha1 'implémentation' com.android.support:appcompat-v7:28.0.0-alpha1 'implémentation' com.android.support.constraint: constraint-layout: 1.1.0 '// Ajoute la dépendance RxJava // implémentation' io.reactivex.rxjava2: rxjava: 2.1.9 '// Ajoute la dépendance RxAndroid // // implémentation' io.reactivex.rxjava2: rxandroid: 2.0.2 '// Ajoute la dépendance RxBinding // implémentation' com.jakewharton.rxbinding2: Rxbinding: 2.1.1 '

Vous pouvez tester cette partie de votre projet en l'installant sur votre smartphone ou tablette Android physique ou sur votre périphérique virtuel Android (AVD). Sélectionnez le enterEmail Éditer le texte et commencez à taper; une Pain grillé devrait apparaître lorsque vous arrêtez de taper. 

Validation de la saisie de l'utilisateur avec les fonctions de transformation

Ensuite, nous devons définir quelques règles de base concernant le type d'entrée que notre application acceptera, puis comparer les entrées de l'utilisateur à ce critère et afficher un message d'erreur, le cas échéant.. 

Vérifier l'adresse e-mail ou le mot de passe de l'utilisateur est un processus en plusieurs étapes. Par conséquent, pour rendre notre code plus facile à lire, je vais combiner toutes ces étapes dans le leur. fonction de transformation. 

Voici le début de la email validé fonction de transformation:

// Définir un observableTransformer. L'entrée et la sortie doivent être une chaîne // valeur privée val validateEmailAddress = ObservableTransformer observable -> // Utilisez flatMap pour appliquer une fonction à chaque élément émis par Observable // observable.flatMap // Coupez les espaces blancs au début et à la fin de l'entrée de l'utilisateur // Observable.just (it) .map  it.trim () // Vérifie si l'entrée correspond au modèle de courrier électronique d'Android // .filter Patterns.EMAIL_ADDRESS.matcher (it) .matches ()

Dans le code ci-dessus, nous utilisons le filtre() opérateur pour filtrer la sortie de l'Observable en fonction de son adéquation avec celle d'Android Patterns.EMAIL_ADDRESS modèle.

Dans la partie suivante de la fonction de transformation, nous devons spécifier ce qui se passe si l’entrée ne correspond pas à la ADRESSE ÉLECTRONIQUE modèle. Par défaut, chaque erreur irrécupérable déclenchera un appel à onError (), qui termine le flux de données. Au lieu de mettre fin au flux, nous voulons que notre application affiche un message d'erreur. Je vais donc utiliser onErrorResumeNext, qui ordonne à l'Observable de réagir à une erreur en passant le contrôle à un nouvel Observable, plutôt que d'invoquer onError (). Cela nous permet d'afficher notre message d'erreur personnalisé.

// Si l'entrée de l'utilisateur ne correspond pas au modèle de courrier électronique, émettez une erreur // .singleOrError () .onErrorResumeNext if (c'est NoSuchElementException) Single.error (Exception ("Veuillez entrer une adresse email valide"))  else Single.error (it) .toObservable ()

La dernière étape consiste à appliquer cette fonction de transformation au flux de données de courrier électronique, en utilisant le .composer() opérateur. À ce stade, votre MainActivity.kt devrait ressembler à ceci: 

import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Patterns import io.reactivex.Observable import io.reactivex.ObservableTransformer import io.reactivex.Observable importation import kotlinx.android.synthetic.main.activity_main. * import java.util.concurrent.TimeUnit import com.jakewharton.rxbinding2.widget.RxTextView class MainActivity: AppCompatActivity () annule le plaisir surCreate (savedInstanceState: Bundle?) (savedInstanceState) setContentView (R.layout.activity_main) RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .map emailError.error = null it.view (). text.toString () .debounce (400, // Assurez-vous nous sommes dans le fil principal de l'interface utilisateur d'Android // TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .compose (validateEmailAddress) .compose (retryWhenError passwordError.error = it.message) .subscribe () // If l'application rencontre une erreur, puis réessayez // fu inline privé n retryWhenError (crossinline onError: (ex: Throwable) -> Unit): ObservableTransformer = ObservableTransformer observable -> observable.retryWhen errors -> // Utilisez l'opérateur flatmap () pour aplatir toutes les émissions en un seul observable // errors.flatMap onError (it) Observable.just ("") / / Définir un ObservableTransformer, où nous effectuerons la validation de l’e-mail // valeur privée val validateEmailAddress = ObservableTransformer observable -> observable.flatMap Observable.just (it) .map it.trim () // Vérifie si la saisie de l'utilisateur correspond au modèle de courrier électronique d'Android // .filter Patterns.EMAIL_ADDRESS.matcher (it) .matches ( ) // Si l'entrée de l'utilisateur ne correspond pas au modèle de courrier électronique, lancez une erreur // .singleOrError () .onErrorResumeNext if (c'est NoSuchElementException) Single.error (Exception ("Entrez une adresse email valide" )) else Single.error (it) .toObservable ()

Installez ce projet sur votre appareil Android ou AVD et vous constaterez que la partie courrier électronique du S'inscrire l'écran vérifie maintenant votre saisie avec succès. Essayez d'entrer autre chose qu'une adresse e-mail et l'application vous avertira que cette entrée n'est pas valide.. 

Rinçage et répétition: vérification du mot de passe de l'utilisateur

À ce stade, nous avons un système entièrement fonctionnel. enterEmail terrain et mise en œuvre Entrer le mot de passe est principalement juste un cas de répéter les mêmes étapes.

En fait, la seule différence majeure est que notre validatePassword La fonction de transformation doit vérifier différents critères. Je vais préciser que le mot de passe de l'utilisateur doit contenir au moins 7 caractères: 

 .filtre it.length> 7

Après avoir répété toutes les étapes précédentes, la tâche terminée MainActivity.kt devrait ressembler à ceci: 

import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Patterns import io.reactivex.Observable import io.reactivex.ObservableTransformer import io.reactivex.Observable importation import kotlinx.android.synthetic.main.activity_main. * import java.util.concurrent.TimeUnit import com.jakewharton.rxbinding2.widget.RxTextView class MainActivity: AppCompatActivity () annule le plaisir surCreate (savedInstanceState: Bundle?) (savedInstanceState) setContentView (R.layout.activity_main) // Répondre aux événements de modification de texte dans enterEmail // RxTextView.afterTextChangeEvents (enterEmail) // Ignorer l'état initial vide de enterEmail // .skipInitialValue () // Transformer les données émises / / .map emailError.error = null // Convertit l'entrée utilisateur en chaîne // it.view (). text.toString () // Ignore toutes les émissions qui se produisent dans un délai de 400 millisecondes // .debounce (400 , // Assurez-vous que nous sommes dans le fil principal de l'interface utilisateur d'Android // Tim eUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) // Application de la fonction de transformation validateEmailAddress // .compose (validateEmailAddress) // Application de la fonction de transformation retryWhenError // .compose (retryWhenError emailError.error) .subscribe () // Rincez et répétez pour 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 () // Si l'application génère une erreur, réessayez / / private inline fun retryWhenError (crossinline onError: (ex: Throwable) -> Unit): ObservableTransformer = ObservableTransformer observable -> observable.retryWhen erreurs -> /// Utilisez l'opérateur flatmap () pour aplatir toutes les émissions en un seul observable // errors.flatMap onError (it) Observable.just ("") // Définit notre ObservableTransformer et spécifie que l'entrée et la sortie doivent être une chaîne // private val validatePassword = ObservableTransformer observable -> observable.flatMap Observable.just (it) .map it.trim () // Autorise uniquement les mots de passe d'au moins 7 caractères // .filter it.length> 7 // Si le Si le mot de passe est inférieur à 7 caractères, générez une erreur // .singleOrError () // Si une erreur se produit… // .onErrorResumeNext if (c'est NoSuchElementException) // Affiche le message suivant dans le passwordError TextInputLayout // Single. error (Exception ("Votre mot de passe doit comporter au moins 7 caractères")) Single.error (it) .toObservable () // Définit un ObservableTransformer, où nous effectuerons la validation de l'e-mail // privé. val validateEmailAddress = ObservableTransformer observable -> observable.flatMap Observable.just (it) .map it.trim () // Vérifie si la saisie de l'utilisateur correspond au modèle de courrier électronique d'Android // .filter Patterns.EMAIL_ADDRESS.matcher (it) .matches ( ) // Si l'entrée de l'utilisateur ne correspond pas au modèle d'e-mail… // .singleOrError () .onErrorResumeNext if (c'est NoSuchElementException) //// Affiche le message suivant dans l'emailError TextInputLayout // Single.error ( Exception ("Entrez une adresse électronique valide")) else Single.error (it) .toObservable ()

Installez ce projet sur votre appareil Android ou AVD et expérimentez la saisie au clavier. enterEmail et Entrer le mot de passe des champs. Si vous entrez une valeur qui ne répond pas aux exigences de l'application, le message d'avertissement correspondant s'affichera., sans pour autant vous devez appuyer sur le S'inscrire bouton.

Vous pouvez télécharger ce projet complet depuis GitHub.

Conclusion

Dans cet article, nous avons examiné comment RxJava peut aider à résoudre les problèmes concrets que vous rencontrerez lors du développement de vos propres applications Android, en utilisant RxJava 2.0, RxBinding et RxAndroid pour créer une S'inscrire écran. 

Pour plus d'informations générales sur la bibliothèque RxJava, consultez notre article Premiers pas avec RxJava 2.0..