Programación reactiva de Kotlin para una pantalla de registro de Android
() translation by (you can also view the original English article)
Programación reactiva de Kotlin para una pantalla de registro de AndroidRxJava 2.0 es una popular biblioteca de programación reactiva que ha ayudado a innumerables desarrolladores de Android a crear aplicaciones altamente receptivas, utilizando menos código y menos complejidad, especialmente cuando se trata de administrar múltiples hilos.
Si es uno de los muchos desarrolladores que han hecho el cambio a Kotlin, ¡no significa que deba renunciar a RxJava!
En la primera parte de esta serie, le mostré cómo pasar de la programación con RxJava 2.0 en Java a la programación con RxJava en Kotlin. También vimos cómo desterrar los estándares de sus proyectos al aprovechar las funciones de extensión de RxKotlin y el secreto para evitar el problema de conversión de SAM que muchos desarrolladores encuentran cuando comienzan a utilizar RxJava 2.0 con Kotlin.
En esta segunda entrega, nos concentraremos en cómo RxJava puede ayudar a resolver los problemas que encontrará en los proyectos reales de Android, mediante la creación de una aplicación reactiva de Android utilizando RxJava 2.0, RxAndroid y RxBinding.
¿Cómo puedo usar RxJava en proyectos del mundo real?
En
nuestro artículo Programacion Reactiva con RxJava y RxKotlin, creamos
algunos Observables
y Observers
simples que imprimen datos en el
Logcat de Android Studio, pero esta no es la forma en que utilizará
RxJava en el mundo real.
En este artículo, voy a mostrarte cómo usar RxJava para crear una pantalla que se usa en innumerables aplicaciones de Android: la clásica pantalla de registro.



Si su aplicación tiene algún tipo de experiencia de suscripción, normalmente tendrá reglas estrictas sobre el tipo de información que acepta. Por ejemplo, tal vez la contraseña deba exceder una cierta cantidad de caracteres, o la dirección de correo electrónico debe estar en un formato de correo electrónico válido.
Si bien puede verificar la entrada del usuario una vez que presiona el botón Sign Up, esta no es la mejor experiencia de usuario, ya que los deja abiertos para enviar información que claramente nunca será aceptada por su aplicación.
Es mucho mejor monitorear al usuario a medida que escribe, y luego avisarle tan pronto como quede claro que está ingresando información que no cumple con los requisitos de su aplicación. Al proporcionar este tipo de comentarios en vivo y en curso, le da al usuario la oportunidad de corregir sus errores antes de presionar ese botón Sign Up.
Si bien puede monitorear la actividad de los usuarios utilizando vanot Kotlin, podemos ofrecer esta funcionalidad utilizando mucho menos código al contar con la ayuda de RxJava, además de algunas otras bibliotecas relacionadas.
Creando la interfaz de usuario
Comencemos construyendo nuestra interfaz de usuario. Voy a agregar lo siguiente:
- Dos
EditTexts
, donde el usuario puede ingresar su dirección de correo electrónico (enterEmail
) y contraseña (enterPassword
). - Dos
contenedores
TextInputLayout
, que rodearán nuestroenterEmail
yenterPassword
EditTexts
. Estos contenedores mostrarán una advertencia cada vez que el usuario ingrese una dirección de correo electrónico o una contraseña que no cumpla con los requisitos de nuestra aplicación. - Un botón de visibilidad de contraseña, que permite al usuario alternar entre enmascarar la contraseña y verla como texto sin formato.
- Un botón Sign up. Para ayudar a mantener este ejemplo centrado en RxJava, no implementaré esta parte de la experiencia de registro, por lo que marcaré este botón como deshabilitado.
Aquí está mi diseño terminado:
1 |
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android" |
2 |
xmlns:app="http://schemas.android.com/apk/res-auto" |
3 |
xmlns:tools="http://schemas.android.com/tools" |
4 |
android:id="@+id/linearLayout" |
5 |
android:layout_width="match_parent" |
6 |
android:layout_height="match_parent" |
7 |
android:orientation="vertical"> |
8 |
|
9 |
<TextView
|
10 |
android:id="@+id/signUp" |
11 |
android:layout_width="wrap_content" |
12 |
android:layout_height="34dp" |
13 |
android:layout_marginTop="16dp" |
14 |
android:gravity="center" |
15 |
android:text="Sign up for an account" |
16 |
android:textColor="#D81B60" |
17 |
android:textSize="25sp" |
18 |
app:layout_constraintEnd_toEndOf="parent" |
19 |
app:layout_constraintStart_toStartOf="parent" |
20 |
app:layout_constraintTop_toTopOf="parent" /> |
21 |
|
22 |
<android.support.design.widget.TextInputLayout
|
23 |
android:id="@+id/emailError" |
24 |
android:layout_width="match_parent" |
25 |
android:layout_height="81dp" |
26 |
app:layout_constraintBottom_toTopOf="@+id/passwordError" |
27 |
app:layout_constraintTop_toBottomOf="@+id/signUp" |
28 |
app:layout_constraintVertical_bias="0.100000024" |
29 |
app:layout_constraintVertical_chainStyle="packed" |
30 |
tools:layout_editor_absoluteX="0dp"> |
31 |
|
32 |
<EditText
|
33 |
android:id="@+id/enterEmail" |
34 |
android:layout_width="match_parent" |
35 |
android:layout_height="wrap_content" |
36 |
android:hint="Email address" |
37 |
android:inputType="textEmailAddress" /> |
38 |
|
39 |
</android.support.design.widget.TextInputLayout>
|
40 |
|
41 |
<android.support.design.widget.TextInputLayout
|
42 |
android:id="@+id/passwordError" |
43 |
android:layout_width="match_parent" |
44 |
android:layout_height="wrap_content" |
45 |
android:layout_marginEnd="10dp" |
46 |
app:layout_constraintBottom_toTopOf="@+id/buttonSignUp" |
47 |
app:layout_constraintStart_toStartOf="parent" |
48 |
app:layout_constraintTop_toBottomOf="@+id/emailError" |
49 |
app:passwordToggleEnabled="true"> |
50 |
|
51 |
<EditText
|
52 |
android:id="@+id/enterPassword" |
53 |
android:layout_width="392dp" |
54 |
android:layout_height="wrap_content" |
55 |
android:hint="Create your password" |
56 |
android:inputType="textPassword" /> |
57 |
|
58 |
</android.support.design.widget.TextInputLayout>
|
59 |
|
60 |
<Button
|
61 |
android:id="@+id/buttonSignUp" |
62 |
android:layout_width="match_parent" |
63 |
android:layout_height="wrap_content" |
64 |
android:background="#0000FF" |
65 |
android:enabled="false" |
66 |
android:text="Sign Up" |
67 |
android:textColor="@android:color/white" |
68 |
app:layout_constraintBottom_toBottomOf="parent" /> |
69 |
|
70 |
</android.support.constraint.ConstraintLayout>
|
Si lo desea, puede copiar / pegar esto en su aplicación, o simplemente puede descargar el código fuente del proyecto desde nuestro repositorio de GitHub.
Creación de una experiencia de inicio de sesión reactiva con Kotlin
Ahora veamos cómo podemos usar RxJava, más algunas bibliotecas relacionadas, para monitorear las opiniones de los usuarios y proporcionar comentarios en tiempo real.
Abordaré la pantalla Sign Up en dos partes. En la primera sección, te mostraré cómo usar la biblioteca RxBinding para registrarte y responder a eventos de cambio de texto. En la segunda sección, crearemos algunas funciones de transformación que validan la entrada del usuario y luego mostraremos un mensaje de error cuando corresponda.
Cree un nuevo proyecto con la configuración de su elección, pero cuando se le solicite, asegúrese de seleccionar la casilla de verificación Include Kotlin Support.
Respondiendo a eventos de cambio de texto
En esta sección, implementaremos la siguiente funcionalidad:
- Detectar cuando
el usuario está escribiendo en el campo
enterEmail
. - Ignore todos los eventos de cambio de texto que ocurren dentro de un corto espacio de tiempo, ya que esto indica que el usuario todavía está escribiendo.
- Realice una acción cuando el usuario deje de escribir.
En nuestra aplicación terminada, aquí es donde validaremos la entrada
del usuario, pero en esta sección solo mostraré un
Toast
.
1. RxBinding
RxBinding es una biblioteca que facilita la conversión de una amplia gama de eventos de IU en Observables, en cuyo punto puede tratarlos como cualquier otro flujo de datos RxJava.
Vamos
a monitorear los eventos de cambio de texto, combinando el widget.RxTextView
con el método afterTextChangeEvents
, por ejemplo:
1 |
RxTextView.afterTextChangeEvents(enterEmail) |
El
problema con tratar los eventos de cambio de texto como flujos de datos
es que inicialmente tanto enterEmail
como enterPassword EditTexts
estarán vacíos, y no queremos que nuestra aplicación reaccione a este
estado vacío como si fuera la primera emisión de datos en el flujo. RxBinding
resuelve este problema al proporcionar un método skipInitialValue()
,
que usaremos para indicar a cada observador que ignore el valor inicial
de su flujo.
1 |
RxTextView.afterTextChangeEvents(enterEmail) |
2 |
.skipInitialValue() |
Miro la biblioteca RxBinding con mayor detalle en mi artículo de RxJava 2 para aplicaciones de Android.
2. Operador de .debounce()
de RxJava
Para ofrecer la mejor experiencia al usuario, debemos mostrar cualquier contraseña o advertencia de correo electrónico relevante después de que el usuario haya terminado de escribir, pero antes de que presionen el botón Sign Up.
Sin RxJava, la identificación de
esta estrecha ventana de tiempo normalmente nos requeriría implementar
un Timer
, pero en RxJava solo necesitamos aplicar el operador
debounce()
a nuestro flujo de datos.
Voy a utilizar
el operador debounce()
para filtrar todos los eventos de cambio de texto
que ocurren en sucesión rápida, es decir, cuando el usuario aún está
escribiendo. Aquí, ignoramos todos los eventos de cambio de texto que ocurren dentro de la misma ventana de 400 milisegundos:
1 |
RxTextView.afterTextChangeEvents(enterEmail) |
2 |
.skipInitialValue() |
3 |
.debounce(400, TimeUnit.MILLISECONDS) |
3. AndroidSchedulers.mainThread()
de RxAndroid ()
El archivo
AndroidSchedulers.mainThread
de la biblioteca RxAndroid nos brinda una
manera fácil de cambiar a la principal hebra de interfaz de usuario
principal de Android.
Dado que solo es
posible actualizar la interfaz de usuario de Android desde el hilo
principal de la IU, debemos asegurarnos de estar en este hilo antes de
intentar mostrar cualquier advertencia de correo electrónico o
contraseña, y antes de mostrar nuestro Toast
.
1 |
RxTextView.afterTextChangeEvents(enterEmail) |
2 |
.skipInitialValue() |
3 |
.debounce(400, TimeUnit.MILLISECONDS) |
4 |
.observeOn(AndroidSchedulers.mainThread()) |
4. Suscribir
Para recibir los datos que emite enterEmail
, debemos suscribirnos a ellos:
1 |
RxTextView.afterTextChangeEvents(enterEmail) |
2 |
.skipInitialValue() |
3 |
.debounce(400, TimeUnit.MILLISECONDS) |
4 |
.observeOn(AndroidSchedulers.mainThread()) |
5 |
.subscribe { |
5. Mostrar Toast
Eventualmente,
queremos que nuestra aplicación responda a los eventos de cambio de
texto validando la entrada del usuario, pero para ayudar a mantener las
cosas claras, en este punto simplemente voy a mostrar un Toast
.
Su código debe verse algo como esto:
1 |
import android.support.v7.app.AppCompatActivity |
2 |
import android.os.Bundle |
3 |
import android.widget.Toast |
4 |
import com.jakewharton.rxbinding2.widget.RxTextView |
5 |
import kotlinx.android.synthetic.main.activity_main.* |
6 |
import io.reactivex.android.schedulers.AndroidSchedulers |
7 |
import java.util.concurrent.TimeUnit |
8 |
|
9 |
class MainActivity : AppCompatActivity() { |
10 |
|
11 |
override fun onCreate(savedInstanceState: Bundle?) { |
12 |
super.onCreate(savedInstanceState) |
13 |
setContentView(R.layout.activity_main) |
14 |
|
15 |
RxTextView.afterTextChangeEvents(enterEmail) |
16 |
.skipInitialValue() |
17 |
.debounce(400, TimeUnit.MILLISECONDS) |
18 |
.observeOn(AndroidSchedulers.mainThread()) |
19 |
.subscribe { |
20 |
Toast.makeText(this, "400 milliseconds since last text change", Toast.LENGTH_SHORT).show() |
21 |
|
22 |
|
23 |
}
|
24 |
}
|
25 |
}
|
6. Actualice sus dependencias
Dado que estamos utilizando algunas bibliotecas diferentes, debemos abrir el archivo build.gradle de nuestro proyecto y agregar RxJava, RxBinding y RxAndroid como dependencias de proyectos:
1 |
dependencies { |
2 |
implementation fileTree(dir: 'libs', include: ['*.jar']) |
3 |
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" |
4 |
implementation 'com.android.support:design:28.0.0-alpha1' |
5 |
implementation 'com.android.support:appcompat-v7:28.0.0-alpha1' |
6 |
implementation 'com.android.support.constraint:constraint-layout:1.1.0' |
7 |
|
8 |
//Add the RxJava dependency// |
9 |
|
10 |
implementation 'io.reactivex.rxjava2:rxjava:2.1.9' |
11 |
|
12 |
//Add the RxAndroid dependency// |
13 |
|
14 |
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' |
15 |
|
16 |
//Add the RxBinding dependency// |
17 |
|
18 |
implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1' |
19 |
|
20 |
} |
Puede probar esta parte de su proyecto instalándola en su teléfono o tableta física Android o dispositivo virtual Android (AVD). Seleccione enterEmail
EditText
y comience a escribir; Debe aparecer Toast
cuando dejes de escribir.



Validación de la entrada del usuario con funciones de transformación
A continuación, debemos establecer algunas reglas básicas sobre el tipo de entrada que nuestra aplicación aceptará, y luego verificar la entrada del usuario con este criterio y mostrar un mensaje de error cuando corresponda.
Verificar el correo electrónico o la contraseña del usuario es un proceso de varios pasos, de modo que para que nuestro código sea más fácil de leer, voy a combinar todos estos pasos en su propia función de transformación.
Aquí
está el inicio de la función de transformación validateEmail
:
1 |
//Define an ObservableTransformer. Input and output must be a string//
|
2 |
|
3 |
private val validateEmailAddress = ObservableTransformer<String, String> { observable -> |
4 |
|
5 |
//Use flatMap to apply a function to every item emitted by the Observable//
|
6 |
|
7 |
observable.flatMap { |
8 |
|
9 |
//Trim any whitespace at the beginning and end of the user’s input//
|
10 |
|
11 |
Observable.just(it).map { it.trim() } |
12 |
|
13 |
//Check whether the input matches Android’s email pattern//
|
14 |
|
15 |
.filter { |
16 |
Patterns.EMAIL_ADDRESS.matcher(it).matches() |
17 |
|
18 |
}
|
En el
código anterior, estamos usando el operador de filter()
para filtrar la
salida del Observable en función de si coincide con el patrón de
Patterns.EMAIL_ADDRESS
de Android.
En la siguiente
parte de la función de transformación, debemos especificar qué sucede si
la entrada no coincide con el patrón EMAIL_ADDRESS
. De forma
predeterminada, cada error irrecuperable activará una llamada a onError()
, que termina la secuencia de datos. En
lugar de finalizar la transmisión, queremos que nuestra aplicación
muestre un mensaje de error, así que usaré onErrorResumeNext
, que indica
al observable que responda a un error al pasar el control a un nuevo
observable, en lugar de invocar a onError()
. Esto nos permite mostrar
nuestro mensaje de error personalizado.
1 |
//If the user’s input doesn’t match the email pattern, then throw an error//
|
2 |
|
3 |
.singleOrError() |
4 |
.onErrorResumeNext { |
5 |
if (it is NoSuchElementException) { |
6 |
Single.error(Exception("Please enter a valid email address")) |
7 |
} else { |
8 |
Single.error(it) |
9 |
|
10 |
}
|
11 |
}
|
12 |
.toObservable() |
13 |
}
|
14 |
}
|
El último
paso es aplicar esta función de transformación al flujo de datos de
correo electrónico, utilizando el operador .compose()
. En este punto, su MainActivity.kt debería verse así:
1 |
import android.support.v7.app.AppCompatActivity |
2 |
import android.os.Bundle |
3 |
import android.util.Patterns |
4 |
import io.reactivex.Observable |
5 |
import io.reactivex.ObservableTransformer |
6 |
import io.reactivex.Single |
7 |
import io.reactivex.android.schedulers.AndroidSchedulers |
8 |
import kotlinx.android.synthetic.main.activity_main.* |
9 |
import java.util.concurrent.TimeUnit |
10 |
import com.jakewharton.rxbinding2.widget.RxTextView |
11 |
|
12 |
class MainActivity : AppCompatActivity() { |
13 |
|
14 |
override fun onCreate(savedInstanceState: Bundle?) { |
15 |
super.onCreate(savedInstanceState) |
16 |
setContentView(R.layout.activity_main) |
17 |
|
18 |
|
19 |
RxTextView.afterTextChangeEvents(enterEmail) |
20 |
.skipInitialValue() |
21 |
.map { |
22 |
emailError.error = null |
23 |
it.view().text.toString() |
24 |
}
|
25 |
.debounce(400, |
26 |
|
27 |
//Make sure we’re in Android’s main UI thread//
|
28 |
|
29 |
TimeUnit.MILLISECONDS).observeOn(AndroidSchedulers.mainThread()) |
30 |
.compose(validateEmailAddress) |
31 |
.compose(retryWhenError { |
32 |
passwordError.error = it.message |
33 |
})
|
34 |
.subscribe() |
35 |
}
|
36 |
|
37 |
//If the app encounters an error, then try again//
|
38 |
|
39 |
private inline fun retryWhenError(crossinline onError: (ex: Throwable) -> Unit): ObservableTransformer<String, String> = ObservableTransformer { observable -> |
40 |
observable.retryWhen { errors -> |
41 |
|
42 |
//Use the flatmap() operator to flatten all emissions into a single Observable//
|
43 |
|
44 |
errors.flatMap { |
45 |
onError(it) |
46 |
Observable.just("") |
47 |
}
|
48 |
|
49 |
}
|
50 |
}
|
51 |
|
52 |
//Define an ObservableTransformer, where we’ll perform the email validation//
|
53 |
|
54 |
private val validateEmailAddress = ObservableTransformer<String, String> { observable -> |
55 |
observable.flatMap { |
56 |
Observable.just(it).map { it.trim() } |
57 |
|
58 |
//Check whether the user input matches Android’s email pattern//
|
59 |
|
60 |
.filter { |
61 |
Patterns.EMAIL_ADDRESS.matcher(it).matches() |
62 |
|
63 |
}
|
64 |
|
65 |
//If the user’s input doesn’t match the email pattern, then throw an error//
|
66 |
|
67 |
.singleOrError() |
68 |
.onErrorResumeNext { |
69 |
if (it is NoSuchElementException) { |
70 |
Single.error(Exception("Please enter a valid email address")) |
71 |
} else { |
72 |
Single.error(it) |
73 |
|
74 |
}
|
75 |
}
|
76 |
.toObservable() |
77 |
}
|
78 |
}
|
79 |
|
80 |
|
81 |
}
|
Instale este proyecto en su dispositivo Android o AVD, y verá que la porción de correo electrónico de la pantalla de Sign Up ahora está verificando su entrada con éxito. Intenta ingresar cualquier cosa que no sea una dirección de correo electrónico, y la aplicación te advertirá que esta no es una entrada válida.



Enjuagar y repetir: verificar la contraseña del usuario
En
este punto, tenemos un campo enterEmail
en pleno funcionamiento, y la
implementación de enterPassword
es solo un caso de repetir los mismos
pasos.
De hecho, la única diferencia importante es
que nuestra función transformation validatePassword
necesita verificar
diferentes criterios. Voy a especificar que la entrada de contraseña del usuario debe tener al menos 7 caracteres de longitud:
1 |
.filter { it.length > 7 } |
Después de repetir todos los pasos anteriores, el MainActivity.kt completo debe verse más o menos así:
1 |
import android.support.v7.app.AppCompatActivity |
2 |
import android.os.Bundle |
3 |
import android.util.Patterns |
4 |
import io.reactivex.Observable |
5 |
import io.reactivex.ObservableTransformer |
6 |
import io.reactivex.Single |
7 |
import io.reactivex.android.schedulers.AndroidSchedulers |
8 |
import kotlinx.android.synthetic.main.activity_main.* |
9 |
import java.util.concurrent.TimeUnit |
10 |
import com.jakewharton.rxbinding2.widget.RxTextView |
11 |
|
12 |
class MainActivity : AppCompatActivity() { |
13 |
|
14 |
override fun onCreate(savedInstanceState: Bundle?) { |
15 |
super.onCreate(savedInstanceState) |
16 |
setContentView(R.layout.activity_main) |
17 |
|
18 |
//Respond to text change events in enterEmail//
|
19 |
|
20 |
RxTextView.afterTextChangeEvents(enterEmail) |
21 |
|
22 |
//Skip enterEmail’s initial, empty state//
|
23 |
|
24 |
.skipInitialValue() |
25 |
|
26 |
//Transform the data being emitted//
|
27 |
|
28 |
.map { |
29 |
emailError.error = null |
30 |
|
31 |
//Convert the user input to a String//
|
32 |
|
33 |
it.view().text.toString() |
34 |
}
|
35 |
|
36 |
//Ignore all emissions that occur within a 400 milliseconds timespan//
|
37 |
|
38 |
.debounce(400, |
39 |
|
40 |
//Make sure we’re in Android’s main UI thread//
|
41 |
|
42 |
TimeUnit.MILLISECONDS).observeOn(AndroidSchedulers.mainThread()) |
43 |
|
44 |
//Apply the validateEmailAddress transformation function//
|
45 |
|
46 |
.compose(validateEmailAddress) |
47 |
|
48 |
//Apply the retryWhenError transformation function//
|
49 |
|
50 |
.compose(retryWhenError { |
51 |
emailError.error = it.message |
52 |
})
|
53 |
.subscribe() |
54 |
|
55 |
//Rinse and repeat for the enterPassword EditText//
|
56 |
|
57 |
RxTextView.afterTextChangeEvents(enterPassword) |
58 |
.skipInitialValue() |
59 |
.map { |
60 |
passwordError.error = null |
61 |
it.view().text.toString() |
62 |
}
|
63 |
.debounce(400, TimeUnit.MILLISECONDS).observeOn(AndroidSchedulers.mainThread()) |
64 |
.compose(validatePassword) |
65 |
.compose(retryWhenError { |
66 |
passwordError.error = it.message |
67 |
})
|
68 |
.subscribe() |
69 |
}
|
70 |
|
71 |
//If the app encounters an error, then try again//
|
72 |
|
73 |
private inline fun retryWhenError(crossinline onError: (ex: Throwable) -> Unit): ObservableTransformer<String, String> = ObservableTransformer { observable -> |
74 |
observable.retryWhen { errors -> |
75 |
|
76 |
///Use the flatmap() operator to flatten all emissions into a single Observable//
|
77 |
|
78 |
errors.flatMap { |
79 |
onError(it) |
80 |
Observable.just("") |
81 |
}
|
82 |
|
83 |
}
|
84 |
}
|
85 |
|
86 |
//Define our ObservableTransformer and specify that the input and output must be a string//
|
87 |
|
88 |
private val validatePassword = ObservableTransformer<String, String> { observable -> |
89 |
observable.flatMap { |
90 |
Observable.just(it).map { it.trim() } |
91 |
|
92 |
//Only allow passwords that are at least 7 characters long//
|
93 |
|
94 |
.filter { it.length > 7 } |
95 |
|
96 |
//If the password is less than 7 characters, then throw an error//
|
97 |
|
98 |
.singleOrError() |
99 |
|
100 |
//If an error occurs.....//
|
101 |
|
102 |
.onErrorResumeNext { |
103 |
if (it is NoSuchElementException) { |
104 |
|
105 |
//Display the following message in the passwordError TextInputLayout//
|
106 |
|
107 |
Single.error(Exception("Your password must be 7 characters or more")) |
108 |
|
109 |
} else { |
110 |
Single.error(it) |
111 |
}
|
112 |
}
|
113 |
.toObservable() |
114 |
|
115 |
|
116 |
}
|
117 |
|
118 |
}
|
119 |
|
120 |
//Define an ObservableTransformer, where we’ll perform the email validation//
|
121 |
|
122 |
private val validateEmailAddress = ObservableTransformer<String, String> { observable -> |
123 |
observable.flatMap { |
124 |
Observable.just(it).map { it.trim() } |
125 |
|
126 |
//Check whether the user input matches Android’s email pattern//
|
127 |
|
128 |
.filter { |
129 |
Patterns.EMAIL_ADDRESS.matcher(it).matches() |
130 |
|
131 |
}
|
132 |
|
133 |
//If the user’s input doesn’t match the email pattern...//
|
134 |
|
135 |
.singleOrError() |
136 |
.onErrorResumeNext { |
137 |
if (it is NoSuchElementException) { |
138 |
|
139 |
////Display the following message in the emailError TextInputLayout//
|
140 |
|
141 |
Single.error(Exception("Please enter a valid email address")) |
142 |
} else { |
143 |
Single.error(it) |
144 |
}
|
145 |
}
|
146 |
.toObservable() |
147 |
}
|
148 |
}
|
149 |
|
150 |
|
151 |
}
|
Instale este proyecto en su dispositivo Android o AVD, y experimente con tipear en los campos enterEmail
y enterPassword
. Si
ingresa un valor que no cumple con los requisitos de la aplicación,
entonces se mostrará el mensaje de advertencia correspondiente, sin que
tenga que tocar el botón Sign Up.
Puede descargar este proyecto completo de GitHub.
Conclusión
En este artículo, analizamos cómo RxJava puede ayudar a resolver los problemas del mundo real que encontrará al desarrollar sus propias aplicaciones de Android, utilizando RxJava 2.0, RxBinding y RxAndroid para crear una pantalla Sign Up.
Para obtener más información general sobre la biblioteca RxJava, asegúrese de revisar nuestro artículo Comenzar con RxJava 2.0.