Cómo correctamente 2-way-bind numérico a Android editText

Debo estar extrañando algo aquí. Cada ejemplo que he visto con la vinculación de 2 vías de Android se basa en una String en los datos de respaldo para cualquier cosa que pueda ingresar el usuario, como un EditText .

Manejar cualquier cosa que no sea una cadena parece algo … poco elegante. Por ejemplo, si tengo un doble en mi modelo de dominio que necesita ser editable, el mejor enlace que he creado requiere un ViewModel con sorprendentemente una gran cantidad de código para la interfaz entre el modelo y el EditText .

¿Me falta algo clave? ¿Debo realmente necesito 30 líneas de código para interconectar un EditText con un doble? Para el bien de la discusión, consideremos un campo de la moneda, representado como un doble, en un EditText -way bound EditText :

 <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:inputType="numberDecimal" android:text="@={fragModel.startBucks}" android:id="@+id/editText2"/> 

Y aquí está el ViewModel que he tenido que construir para darle al EditText una cadena a la cual enlazar.

 @Bindable private String startBucksString; private double localBucks; public String getStartBucksString() { double domainBucks = cd.getStartBucks(); // Ignore incoming change of less than rounding error if( Math.abs(localBucks - domainBucks) < .001 ) return startBucksString; startBucksString = ""; if( domainBucks != 0) startBucksString = String.format("$%.2f", domainBucks); return startBucksString; } public void setStartBucksString(String inBuckstr) { double calcBucks=0; inBuckstr = inBuckstr.replaceAll( "[^\\d.]", "" ); try { calcBucks = Double.parseDouble(inBuckstr); } catch( NumberFormatException e) { return; } // Neglect outgoing change of less than rounding error if( Math.abs(localBucks - calcBucks) < .001 ) return; startBucksString = String.format("$%.2f", calcBucks); localBucks = calcBucks; cd.setStartBucks(calcBucks); notifyPropertyChanged(BR.startBucksString); } 

Aquí, escribí un ejemplo simple y compilable de enlace de 2 vías con un ViewModel . Ilustra la dificultad que tuve en la actualización continua de un flotador en el modelo de dominio – al final, decidí que no hay forma de hacerlo sin escribir un TextWatcher personalizado para cada campo de dominio.

Mi enfoque es retrasar el método notifyPropertyChanged utilizando Handles. De esta manera, mientras el usuario está escribiendo, el código no se ejecuta, luego de 2,5 segundos después de que el usuario se ha detenido para escribir el último carácter, se llamará notificationPropertyChanged.

El efecto visual es fresco, y el usuario es libre de escribir números como quiera.

Vea estos dos ejemplos:

Use puede utilizar este código compacto (?) Para cada campo:

 // // g1FuelCostYear field // private double g1FuelCostYear; @Bindable public String getG1FuelCostYear() { return Double.valueOf(g1FuelCostYear).toString(); } private Handler hG1FuelCostYearDelay = null; public void setG1FuelCostYear(String g1FuelCostYear) { // Delayed notification hadler creation if (hG1FuelCostYearDelay == null) { hG1FuelCostYearDelay = new Handler() { @Override public void handleMessage(Message msg) { notifyPropertyChanged(it.techgest.airetcc2.BR.g1FuelCostYear); } }; } else { // For each call remove pending notifications hG1FuelCostYearDelay.removeCallbacksAndMessages(null); } // Data conversion logic try { this.g1FuelCostYear = Double.parseDouble(g1FuelCostYear); } catch (Exception ex) { this.g1FuelCostYear = 0.0; log(ex); } // New delayed field notification (other old notifications are removed before) hG1FuelCostYearDelay.sendEmptyMessageDelayed(0,2500); } 

Este código en su lugar es útil cuando se utiliza conversor de moneda o conversor de porcentaje. El usuario puede escribir un doble simple, el código convertido en cadena de moneda. Si el setter es llamado con la cadena de moneda el código es capaz de convertirlo como doble también.

 // // g1FuelCostYear field // private double g1FuelCostYear; @Bindable public String getG1FuelCostYear() { NumberFormat nf = NumberFormat.getCurrencyInstance(); return nf.format(this.g1FuelCostYear); //return Double.valueOf(g1FuelCostYear).toString(); } private Handler hG1FuelCostYearDelay = null; public void setG1FuelCostYear(String g1FuelCostYear) { if (hG1FuelCostYearDelay == null) { hG1FuelCostYearDelay = new Handler() { @Override public void handleMessage(Message msg) { notifyPropertyChanged(it.techgest.airetcc2.BR.g1FuelCostYear); } }; } else { hG1FuelCostYearDelay.removeCallbacksAndMessages(null); } boolean success = false; try { NumberFormat nf = NumberFormat.getCurrencyInstance(); this.g1FuelCostYear = nf.parse(g1FuelCostYear).doubleValue(); success = true; } catch (Exception ex) { this.g1FuelCostYear = 0.0; log(ex); } if (!success) { try { this.g1FuelCostYear = Double.parseDouble(g1FuelCostYear); success = true; } catch (Exception ex) { this.g1FuelCostYear = 0.0; log(ex); } } updateG1FuelConsumption(); hG1FuelCostYearDelay.sendEmptyMessageDelayed(0,2500); } 
  • Enlace de datos: enlaza método único de escucha con múltiples métodos
  • Android DataBinding y MVVM - Utilizar los mismos archivos de diseño para diferentes condiciones utilizando los mismos modelos de vista
  • No se pueden ejecutar pruebas de instrumentación después de introducir el enlace de datos
  • Enlaces de datos con oyentes personalizados en la vista personalizada
  • Error de compilación de enlace de datos de Android: : no se pudo establecer el enlace de datos
  • Cómo acceder a la vista dentro de un diseño incluido mediante el enlace de datos
  • Android Databinding atributo xml duplicado
  • Kotlin-android: enlace de datos de referencia no resuelto
  • Cómo configurar el error en EditText utilizando DataBinding MVMF de Framwork
  • Enlace de datos bidireccional con doble valor en EditText
  • ¿Utilizar recursos de atributos (? Attr /) en el enlace de diseño?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.