RxJava- validación de formulario RxAndroid en dynamic EditText

Tengo un formulario que puede tener un número variable de EditText que necesita ser validado antes de la presentación del formulario. Puedo realizar comprobación de la validación si EditText s se fija en número como el siguiente –

 Observable<CharSequence> emailObservable = RxTextView.textChanges(editEmail).skip(1); Observable<CharSequence> passwordObservable = RxTextView.textChanges(editPassword).skip(1); mFormValidationSubscription = Observable.combineLatest(emailObservable, passwordObservable, (newEmail, newPassword) -> { boolean emailValid = !TextUtils.isEmpty(newEmail) && android.util.Patterns.EMAIL_ADDRESS.matcher(newEmail).matches(); if(!emailValid) { emailInputLayout.setError(getString(R.string.error_invalid_email)); emailInputLayout.setErrorEnabled(true); }else { emailInputLayout.setError(null); emailInputLayout.setErrorEnabled(false); } boolean passValid = !TextUtils.isEmpty(newPassword) && newPassword.length() > 4; if (!passValid) { passwordInputLayout.setError(getString(R.string.error_invalid_password)); passwordInputLayout.setErrorEnabled(true); } else { passwordInputLayout.setError(null); passwordInputLayout.setErrorEnabled(true); } return emailValid && passValid; }).subscribe(isValid ->{ mSubmitButton.setEnabled(isValid); }); 

Pero ahora como hay un número variable de entradas intenté crear una lista de Observable<CharSequence> y Observable.combineLatest() pero estoy atascado como para proceder con eso.

 List<Observable<CharSequence>> observableList = new ArrayList<>(); for(InputRule inputRule : mMaterial.getRules()) { View vInputRow = inflater.inflate(R.layout.item_material_input_row, null, false); StyledEditText styledEditText = ((StyledEditText)vInputRow.findViewById(R.id.edit_input)); styledEditText.setHint(inputRule.getName()); Observable<CharSequence> observable = RxTextView.textChanges(styledEditText).skip(1); observableList.add(observable); linearLayout.addView(vInputRow); } Observable.combineLatest(observableList,......); // What should go in place of these "......" 

¿Cómo puedo realizar comprobaciones para una secuencia de caracteres válida para cada campo de entrada. Miré en los flatMap() , map() , filter() , pero no sé cómo usarlos.

Sí, procesa un número abigarrado de Observables en .combineLatest (), pero todavía hay solución. Me interesé en este problema y surgió con la siguiente solución: podemos almacenar información sobre algún origen de datos, valor pasado y código fuente (String e id de recursos) y tunnell todos los datos en algún tubo común. Para eso podemos usar PublishSubject. También necesitamos rastrear el estado de la conexión, por eso debemos guardar la Suscripción a cada fuente en la suscripción y cortarla cuando nos desinscribamos de esa fuente. Almacenamos los últimos datos de cada fuente, por lo que podemos decirle al usuario qué fuente acaba de emitir un nuevo valor, la devolución de llamada sólo contendrá la identificación de la fuente. El usuario puede obtener el último valor de cualquier fuente por ID de origen. Me ocurrió el siguiente código:

 import android.util.Log; import android.widget.EditText; import com.jakewharton.rxbinding.widget.RxTextView; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import rx.Observable; import rx.Subscription; import rx.functions.Action1; import rx.subjects.PublishSubject; public class MultiSourceCombinator { String LOG_TAG = MultiSourceCombinator.class.getSimpleName(); /** * We can't handle arbitrary number of sources by CombineLatest, but we can pass data along * with information about source (sourceId) */ private static class SourceData{ String data = ""; Integer sourceId = 0; } /** * Keep id of source, subscription to that source and last value emitted * by source. This value is passed when source is attached */ private class SourceInfo{ Subscription sourceTracking; Integer sourceId; SourceData lastData; SourceInfo(int sourceId, String data){ this.sourceId = sourceId; // initialize last data with empty value SourceData d = new SourceData(); d.data = data; d.sourceId = sourceId; this.lastData = d; } } /** * We can tunnel data from all sources into single pipe. Subscriber can treat it as * Observable<SourceData> */ private PublishSubject<SourceData> dataDrain; /** * Stores all sources by their ids. */ Map<Integer, SourceInfo> sources; /** * Callback, notified whenever source emit new data. it receives source id. * When notification is received by client, it can get value from source by using * getLastSourceValue(sourceId) method */ Action1<Integer> sourceUpdateCallback; public MultiSourceCombinator(){ dataDrain = PublishSubject.create(); sources = new HashMap<>(); sourceUpdateCallback = null; // We have to process data, ccoming from common pipe dataDrain.asObservable() .subscribe(newValue -> { if (sourceUpdateCallback == null) { Log.w(LOG_TAG, "Source " + newValue.sourceId + "emitted new value, " + "but used did't set callback "); } else { sourceUpdateCallback.call(newValue.sourceId); } }); } /** * Disconnect from all sources (sever Connection (s)) */ public void stop(){ Log.i(LOG_TAG, "Unsubscribing from all sources"); // copy references to aboid ConcurrentModificatioinException ArrayList<SourceInfo> t = new ArrayList(sources.values()); for (SourceInfo si : t){ removeSource(si.sourceId); } // right now there must be no active sources if (!sources.isEmpty()){ throw new RuntimeException("There must be no active sources"); } } /** * Create new source from edit field, subscribe to this source and save subscription for * further tracking. * @param editText */ public void addSource(EditText editText, int sourceId){ if (sources.containsKey(sourceId)){ Log.e(LOG_TAG, "Source with id " + sourceId + " already exist"); return; } Observable<CharSequence> source = RxTextView.textChanges(editText).skip(1); String lastValue = editText.getText().toString(); Log.i(LOG_TAG, "Source with id " + sourceId + " has data " + lastValue); // Redirect data coming from source to common pipe, to do that attach source id to // data string Subscription sourceSubscription = source.subscribe(text -> { String s = new String(text.toString()); SourceData nextValue = new SourceData(); nextValue.sourceId = sourceId; nextValue.data = s; Log.i(LOG_TAG, "Source " + sourceId + "emits new value: " + s); // save vlast value sources.get(sourceId).lastData.data = s; // pass new value down pipeline dataDrain.onNext(nextValue); }); // create SourceInfo SourceInfo sourceInfo = new SourceInfo(sourceId, lastValue); sourceInfo.sourceTracking = sourceSubscription; sources.put(sourceId, sourceInfo); } /** * Unsubscribe source from common pipe and remove it from list of sources * @param sourceId * @throws IllegalArgumentException */ public void removeSource(Integer sourceId) throws IllegalArgumentException { if (!sources.containsKey(sourceId)){ throw new IllegalArgumentException("There is no source with id: " + sourceId); } SourceInfo si = sources.get(sourceId); Subscription s = si.sourceTracking; if (null != s && !s.isUnsubscribed()){ Log.i(LOG_TAG, "source " + sourceId + " is active, unsubscribing from it"); si.sourceTracking.unsubscribe(); si.sourceTracking = null; } // source is disabled, remove it from list Log.i(LOG_TAG, "Source " + sourceId + " is disabled "); sources.remove(sourceId); } /** * User can get value from any source by using source ID. * @param sourceId * @return * @throws IllegalArgumentException */ public String getLastSourceValue(Integer sourceId) throws IllegalArgumentException{ if (!sources.containsKey(sourceId)){ throw new IllegalArgumentException("There is no source with id: " + sourceId); } String lastValue = sources.get(sourceId).lastData.data; return lastValue; } public void setSourceUpdateCallback(Action1<Integer> sourceUpdateFeedback) { this.sourceUpdateCallback = sourceUpdateFeedback; } } 

Y podemos usarlo en la interfaz de usuario de esta manera:

 import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.widget.EditText; import android.widget.Toast; import butterknife.BindView; import butterknife.ButterKnife; public class EdiTextTestActivity extends Activity { @BindView(R.id.aet_et1) public EditText et1; @BindView(R.id.aet_et2) public EditText et2; @BindView(R.id.aet_et3) public EditText et3; private MultiSourceCombinator multiSourceCombinator; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_edit_text_test); ButterKnife.bind(this); multiSourceCombinator = new MultiSourceCombinator(); multiSourceCombinator.setSourceUpdateCallback(id -> { Toast.makeText(EdiTextTestActivity.this, "New value from source: " + id + " : " + multiSourceCombinator.getLastSourceValue(id), Toast.LENGTH_SHORT).show(); }); } @Override protected void onPause() { // stop tracking all fields multiSourceCombinator.stop(); super.onPause(); } @Override protected void onResume() { super.onResume(); // Register fields multiSourceCombinator.addSource(et1, R.id.aet_et1); multiSourceCombinator.addSource(et2, R.id.aet_et2); multiSourceCombinator.addSource(et3, R.id.aet_et3); } } 

Tengo una solución para usted sin usar expresiones lambda (como no podía compilarlo con lambdas).

Utilice el mismo operador que desee:

public static <T, R> Observable<R> combineLatest(List<? extends Observable<? extends T>> sources, FuncN<? extends R> combineFunction)

 Observable.combineLatest(observableList, new FuncN<Boolean>() { @Override public Boolean call(Object... objects) { boolean isValid = true; CharSequence input; for (int i = 0; i < objects.length; i++) { input = (CharSequence) objects[i]; switch (i) { case 1: //First text field value break; case 2: //Second text field value break; default: isValid = false; } } return isValid; } }) 

La razón por la que las expresiones lambda no funcionan probablemente está en el segundo parámetro de la función combineLatest(...) :

 public interface FuncN<R> extends Function { R call(Object... args); } 

De acuerdo con este post la implementación de Arbitrary Número de Argumentos es difícil de hacer y soluciones deben ser creados. RxJava v2 es compatible con Java 8 y tiene una implementación diferente de combineLatest

  • Añadir una anotación personalizada a Android Saripaar
  • Validación de correo electrónico en EditText - Android
  • Cómo validar String usando expresión regular en java
  • Mostrar error si el usuario introduce sólo espacios en EditText - Android
  • Centrarse en segundo edittext sólo si primero es no-vacío android
  • Validación en Editar texto
  • Cómo validar un nombre de URL / sitio web en EditText en Android?
  • ¿No se muestran los indicadores de error (para validaciones de formularios) para android 4.2?
  • Cómo restringir el EditText para aceptar sólo caracteres alfanuméricos
  • ¿Cómo determinar si una entrada en EditText es un entero?
  • ¿Cuál es una buena manera de limitar el número de palabras que se pueden introducir en una vista de edición de Android?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.