Kotlin Reactive Programming per una schermata di accesso Android

RxJava 2.0 è una popolare libreria di programmazione reattiva che ha aiutato innumerevoli sviluppatori Android a creare applicazioni altamente reattive, utilizzando meno codice e meno complessità, particolarmente quando si tratta di gestire più thread.

Se sei uno dei tanti sviluppatori che ha fatto il passaggio a Kotlin, allora non significa che devi rinunciare a RxJava! 

Nella prima parte di questa serie, vi ho mostrato come passare dalla programmazione con RxJava 2.0 in Java alla programmazione con RxJava in Kotlin. Abbiamo anche esaminato come bandire lo standard di stampa dai tuoi progetti sfruttando le funzioni di estensione di RxKotlin e il segreto per evitare il problema di conversione SAM che molti sviluppatori incontrano quando iniziano a utilizzare RxJava 2.0 con Kotlin. 

In questa seconda puntata, ci concentreremo su come RxJava può aiutare a risolvere i problemi che incontrerai nei progetti Android reali, creando un'applicazione Android reattiva usando RxJava 2.0, RxAndroid e RxBinding.

Come posso utilizzare RxJava in progetti Real-World?

Nella nostra programmazione reattiva con articolo RxJava e RxKotlin, abbiamo creato alcuni semplici osservabili e Gli osservatori che stampa i dati su Android Studio logcat-ma questo non è il modo in cui userete RxJava nel mondo reale.

In questo articolo, ti mostrerò come utilizzare RxJava per creare uno schermo che viene utilizzato in innumerevoli applicazioni Android: il classico Iscriviti schermo. 

Se la tua app ha qualunque tipo di esperienza di iscrizione, quindi in genere avrà regole severe sul tipo di informazioni che accetta. Ad esempio, forse la password deve superare un certo numero di caratteri, o l'indirizzo e-mail deve essere in un formato e-mail valido.

Mentre tu poteva controlla l'input dell'utente una volta colpito Iscriviti pulsante, questa non è la migliore esperienza utente, in quanto lascia aperta la possibilità di inviare informazioni che non saranno mai accettate dall'applicazione.

È molto meglio monitorare l'utente mentre sta digitando, e quindi dare loro un colpo d'occhio non appena diventa chiaro che stanno inserendo informazioni che non soddisfano i requisiti della tua app. Fornendo questo tipo di feedback in tempo reale e continuo, offrite all'utente la possibilità di correggere i propri errori prima colpendolo Iscriviti pulsante.

Mentre tu poteva monitorare l'attività degli utenti utilizzando vanilla Kotlin, possiamo fornire questa funzionalità utilizzando molto meno codice arruolando l'aiuto di RxJava, più alcune altre librerie correlate.

Creazione dell'interfaccia utente

Iniziamo creando la nostra interfaccia utente. Ho intenzione di aggiungere il seguente:

  • Due EditTexts, dove l'utente può inserire il proprio indirizzo email (Inserisci l'email) e password (inserire la password).
  • Due TextInputLayout involucri che circonderanno il nostro Inserisci l'email e inserire la password EditTexts. Questi wrapper visualizzeranno un avviso ogni volta che l'utente immette un indirizzo email o una password che non soddisfano i requisiti della nostra app.
  • Un pulsante di visibilità della password, che consente all'utente di passare dal mascherare la password e visualizzarla come testo normale.
  • UN Iscriviti pulsante. Per mantenere questo esempio focalizzato su RxJava, non implementerò questa parte dell'esperienza di registrazione, quindi contrassegnerò questo pulsante come disabilitato.

Ecco il mio layout finito:

        

Puoi copiare / incollare questo nella tua app se lo desideri, oppure puoi semplicemente scaricare il codice sorgente del progetto dal nostro repository GitHub.

Creazione di un'esperienza di accesso reattiva con Kotlin

Ora vediamo come possiamo usare RxJava, più alcune librerie correlate, per monitorare l'input dell'utente e fornire feedback in tempo reale. 

Affronterò il Iscriviti schermo in due parti. Nella prima sezione, ti mostrerò come utilizzare la libreria RxBinding per registrare e rispondere agli eventi di modifica del testo. Nella seconda sezione creeremo alcune funzioni di trasformazione che convalidano l'input dell'utente e quindi visualizzeranno un messaggio di errore dove appropriato.

Crea un nuovo progetto con le impostazioni di tua scelta, ma quando richiesto, assicurati di selezionare il Includi supporto Kotlin casella di controllo. 

Risposta agli eventi di modifica del testo

In questa sezione, implementeremo la seguente funzionalità: 

  • Rileva quando l'utente sta digitando il Inserisci l'email campo.
  • Ignora tutti gli eventi di modifica del testo che si verificano in un breve lasso di tempo, poiché ciò indica che l'utente sta ancora digitando. 
  • Esegui un'azione quando l'utente interrompe la digitazione. Nella nostra app finale, è qui che verificheremo l'input dell'utente, ma in questa sezione visualizzerò solo un Crostini

1. RxBinding 

RxBinding è una libreria che semplifica la conversione di una vasta gamma di eventi UI in oggetti osservabili, a quel punto puoi trattarli come qualsiasi altro flusso di dati RxJava.

Stiamo andando a monitorare gli eventi di modifica del testo, combinando RxBinding widget.RxTextView con il afterTextChangeEvents metodo, ad esempio:

RxTextView.afterTextChangeEvents (enterEmail)

Il problema con il trattamento degli eventi di modifica del testo come flussi di dati è che inizialmente entrambi Inserisci l'email e enterPassword EditTexts sarà vuoto e non vogliamo che la nostra app reagisca a questo stato vuoto come se fosse la prima emissione di dati nel flusso. RxBinding risolve questo problema fornendo a skipInitialValue () metodo, che useremo per istruire ciascun osservatore a ignorare il valore iniziale del proprio stream.

 RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue ()

Guardo la libreria RxBinding in maggior dettaglio nel mio articolo RxJava 2 per app Android.

2. RxJava's .antirimbalzo () Operatore

Per offrire la migliore esperienza utente, è necessario visualizzare qualsiasi password pertinente o avvertimenti e-mail dopo che l'utente ha finito di digitare, ma prima che colpiscano il Iscriviti pulsante.

Senza RxJava, l'individuazione di questa stretta finestra temporale richiederebbe in genere l'implementazione di a Timer, ma in RxJava abbiamo solo bisogno di applicare il antirimbalzo () operatore al nostro flusso di dati.

Userò il antirimbalzo () operatore per filtrare tutti gli eventi di cambio di testo che avvengono in rapida successione, cioè quando l'utente sta ancora digitando. Qui, ignoriamo tutti gli eventi di modifica del testo che si verificano nella stessa finestra di 400 millisecondi:

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

3. RxAndroid's AndroidSchedulers.mainThread ()

La libreria di RxAndroid AndroidSchedulers.mainThread ci offre un modo semplice per passare all'intera sezione principale dell'interfaccia utente di Android.

Poiché è possibile aggiornare l'interfaccia utente di Android solo dal thread principale dell'interfaccia utente, dobbiamo assicurarci di essere in questa discussione prima di tentare di visualizzare gli avvisi di email o password e prima di visualizzare i nostri Crostini

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

4. Iscriviti

Per ricevere i dati emessi da Inserisci l'email, dobbiamo abbonarci ad esso:

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

5. Visualizza il pane tostato

Alla fine, vogliamo che la nostra applicazione risponda agli eventi di modifica del testo convalidando l'input dell'utente, ma per aiutare a mantenere le cose semplici, a questo punto sto semplicemente visualizzando un Crostini

Il tuo codice dovrebbe apparire in questo modo:

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. * import io.reactivex.android .schedulers.AndroidSchedulers import java.util.concurrent.TimeUnit class MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) RxTextView.afterTextChangeEvents (enterEmail). skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .subscribe Toast.makeText (questo, "400 millisecondi dall'ultimo cambio di testo", Toast.LENGTH_SHORT) .show ()

6. Aggiorna le tue dipendenze

Dal momento che stiamo usando alcune librerie diverse, abbiamo bisogno di aprire i nostri progetti build.gradle file e aggiungere RxJava, RxBinding e RxAndroid come dipendenze del progetto: 

dependencies implementation fileTree (dir: 'libs', include: ['* .jar']) implementazione "org.jetbrains.kotlin: kotlin-stdlib-jdk7: $ kotlin_version" implementazione 'com.android.support:design:28.0. 0-alpha1 'implementation' com.android.support:appcompat-v7:28.0.0-alpha1 'implementation' com.android.support.constraint: constraint-layout: 1.1.0 '// Aggiungi la dependency RxJava // implementation' io.reactivex.rxjava2: rxjava: 2.1.9 '// Aggiungi la dipendenza RxAndroid // implementazione' io.reactivex.rxjava2: rxandroid: 2.0.2 '// Aggiungi la dipendenza RxBinding // implementazione' com.jakewharton.rxbinding2: rxbinding: 2.1.1 '

Puoi testare questa parte del tuo progetto installandola sul tuo smartphone o tablet Android fisico o dispositivo virtuale Android (AVD). Seleziona il Inserisci l'email Modifica il testo e inizia a digitare; un Crostini dovrebbe apparire quando smetti di digitare. 

Convalida dell'input dell'utente con le funzioni di trasformazione

Successivamente, abbiamo bisogno di stabilire alcune regole di base sul tipo di input che la nostra applicazione accetterà, quindi verificare l'input dell'utente rispetto a questi criteri e visualizzare un messaggio di errore dove appropriato. 

Controllare l'e-mail o la password dell'utente è un processo in più fasi, quindi per rendere il nostro codice più facile da leggere, ho intenzione di combinare tutti questi passaggi nel loro funzione di trasformazione. 

Ecco l'inizio del validateEmail funzione di trasformazione:

// Definire un ObservableTransformer. Input e output devono essere una stringa // private val validateEmailAddress = ObservableTransformer osservabile -> // Usa flatMap per applicare una funzione a ogni oggetto emesso da Observable // observable.flatMap // Taglia qualsiasi spazio bianco all'inizio e alla fine dell'input dell'utente // Observable.just (it) .map  it.trim () // Controlla se l'input corrisponde al modello di email di Android //. filtro Patterns.EMAIL_ADDRESS.matcher (it) .matches ()

Nel codice sopra, stiamo usando il filtro() operatore per filtrare l'output dell'Osservable in base al fatto che corrisponda a quello di Android Patterns.EMAIL_ADDRESS modello.

Nella parte successiva della funzione di trasformazione, dobbiamo specificare cosa succede se l'input non corrisponde al INDIRIZZO EMAIL modello. Per impostazione predefinita, ogni errore irreversibile attiverà una chiamata a onError (), che termina il flusso di dati. Invece di terminare lo stream, vogliamo che la nostra applicazione visualizzi un messaggio di errore, quindi userò onErrorResumeNext, che istruisce l'Osservabile a rispondere a un errore passando il controllo a un nuovo Osservabile, piuttosto che invocando onError (). Questo ci consente di visualizzare il nostro messaggio di errore personalizzato.

// Se l'input dell'utente non corrisponde al pattern email, quindi genera un errore // .singleOrError (). OnErrorResumeNext if (è NoSuchElementException) Single.error (Exception ("Please enter a email address"))  else Single.error (it) .toObservable ()

Il passaggio finale consiste nell'applicare questa funzione di trasformazione al flusso di dati della posta elettronica, utilizzando il comando .comporre() operatore. A questo punto, il tuo MainActivity.kt dovrebbe assomigliare a questo: 

importare android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Patterns import io.reactivex.Observable import io.reactivex.ObservableTransformer import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.android.synthetic.main.activity_main. * import java.util.concurrent.TimeUnit import com.jakewharton.rxbinding2.widget.RxTextView class MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .map emailError.error = null it.view (). text.toString () .debounce (400, // Assicurarsi che siamo nel thread dell'interfaccia utente principale di Android // TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .compose (validateEmailAddress). .com (retryWhenError passwordError.error = it.message) .subscribe () // Se l'app incontra un errore, quindi riprova // privato inline fu n retryWhenError (crossinline onError: (es: Throwable) -> Unit): ObservableTransformer = ObservableTransformer observable -> observable.retryWhen errors -> // Usa l'operatore flatmap () per appiattire tutte le emissioni in un singolo Observable // errors.flatMap onError (it) Observable.just ("") / / Definisci un ObservableTransformer, dove eseguiremo la convalida dell'email // private val validateEmailAddress = ObservableTransformer observable.just (it) .map it.trim () // Controlla se l'input dell'utente corrisponde al pattern email di Android //. filtro Patterns.EMAIL_ADDRESS.matcher (it) .matches ( ) // Se l'input dell'utente non corrisponde al pattern email, genera un errore // .singleOrError (). OnErrorResumeNext if (è NoSuchElementException) Single.error (Exception ("Please enter a email address") )) else Single.error (it) .toObservable ()

Installa questo progetto sul tuo dispositivo Android o AVD e scoprirai che la parte email di Iscriviti lo schermo sta controllando il tuo input con successo. Prova a inserire qualcosa di diverso da un indirizzo email e l'app ti avvertirà che questo non è un input valido. 

Risciacquo e ripetizione: controllo della password dell'utente

A questo punto, abbiamo un funzionamento completo Inserisci l'email campo e implementazione inserire la password è per lo più solo un caso di ripetere gli stessi passaggi.

In effetti, l'unica grande differenza è che il nostro validatePassword la funzione di trasformazione deve verificare i diversi criteri. Verrà specificato che l'immissione della password dell'utente deve essere lunga almeno 7 caratteri: 

 .filter it.length> 7

Dopo aver ripetuto tutti i passaggi precedenti, il completamento MainActivity.kt dovrebbe assomigliare a questo: 

importare android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Patterns import io.reactivex.Observable import io.reactivex.ObservableTransformer import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.android.synthetic.main.activity_main. * import java.util.concurrent.TimeUnit import com.jakewharton.rxbinding2.widget.RxTextView class MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) // Rispondi agli eventi di modifica del testo in enterEmail // RxTextView.afterTextChangeEvents (enterEmail) // Salta enterEmail iniziale, stato vuoto // .skipInitialValue () // Trasforma i dati emessi / / .map emailError.error = null // Converti l'input dell'utente in una stringa // it.view (). text.toString () // Ignora tutte le emissioni che si verificano entro un intervallo di 400 millisecondi // .debounce (400 , // Assicurati di essere nel thread dell'interfaccia utente principale di Android // Tim eUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) // Applica la funzione di trasformazione validateEmailAddress // .compose (validateEmailAddress) // Applica la funzione di trasformazione retryWhenError // .compose (retryWhenError emailError.error = it.message) .subscribe () // Risciacquo e ripetizione per enterPassword EditText // RxTextView.afterTextChangeEvents (enterPassword) .skipInitialValue () .map passwordError.error = null it.view (). text.toString () .debounce (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .compose (validatePassword). .Com (retryWhenError passwordError.error = it.message) .subscribe () // Se l'app rileva un errore, riprova / / private inline fun retryWhenError (crossinline onError: (es: Throwable) -> Unit): ObservableTransformer = ObservableTransformer observable -> observable.retryWhen errors -> /// Utilizza l'operatore flatmap () per appiattire tutte le emissioni in un singolo Observable // errors.flatMap onError (it) Observable.just ("") // Definisci il nostro ObservableTransformer e specifica che l'input e l'output devono essere una stringa // private val validatePassword = ObservableTransformer observable.just (it) .map it.trim () // Consenti solo password di almeno 7 caratteri //. filtro it.length> 7 // Se la password è inferiore a 7 caratteri, quindi genera un errore // .singleOrError () // Se si verifica un errore ... //. onErrorResumeNext if (è NoSuchElementException) // Visualizza il seguente messaggio in passwordError TextInputLayout // Single. error (Exception ("La tua password deve essere di 7 caratteri o più")) else Single.error (it) .toObservable () // Definisci un ObservableTransformer, dove eseguiremo la convalida dell'email // private val validateEmailAddress = ObservableTransformer observable.just (it) .map it.trim () // Controlla se l'input dell'utente corrisponde al pattern email di Android //. filtro Patterns.EMAIL_ADDRESS.matcher (it) .matches ( ) // Se l'input dell'utente non corrisponde al modello di email ... // .singleOrError (). OnErrorResumeNext if (è NoSuchElementException) //// Visualizza il seguente messaggio nell'emailError TextInputLayout // Single.error ( Eccezione ("Inserire un indirizzo email valido")) else Single.error (it) .toObservable ()

Installa questo progetto sul tuo dispositivo Android o AVD e prova a digitare nella Inserisci l'email e inserire la password campi. Se inserisci un valore che non soddisfa i requisiti dell'app, verrà visualizzato il messaggio di avviso corrispondente, senza devi toccare il Iscriviti pulsante.

Puoi scaricare questo progetto completo da GitHub.

Conclusione

In questo articolo, abbiamo esaminato come RxJava può aiutare a risolvere i problemi reali che incontrerai quando sviluppi le tue applicazioni Android, usando RxJava 2.0, RxBinding e RxAndroid per creare un Iscriviti schermo. 

Per ulteriori informazioni di base sulla libreria RxJava, assicurati di consultare l'articolo Get Started With RxJava 2.0.