2016-05-26 12 views
5

Mi manca qualcosa qui. Ogni esempio che ho visto con il bind a 2 vie di Android si basa su un valore String nei dati di supporto per qualsiasi elemento immesso dall'utente, ad esempio EditText.Come associare correttamente 2-way-bind ad Android editText

Maneggiare qualcosa che non sia una stringa sembra un po '... inelegante. Ad esempio, se ho un double nel mio modello di dominio che deve essere modificabile, la migliore associazione che ho trovato richiede uno ViewModel con sorprendentemente un sacco di codice da interfacciare tra il modello e lo EditText.

Mi manca qualcosa chiave? Dovrei davvero bisogno di 30 linee di codice per interfacciare un EditText con un doppio? Per il bene della discussione, prendiamo in considerazione un campo di valuta, rappresentato come un doppio, in un doppio senso legato EditText:

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

Ed ecco il ViewModel ho avuto di costruire al fine di dare l'EditText una stringa legare a

@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); 
} 

Ecco, ho scritto a simple, compilable example of 2-way binding with a ViewModel. Illustra la difficoltà che ho avuto nell'aggiornare continuamente un float nel modello di dominio - alla fine, ho deciso che non c'è modo di farlo senza scrivere un TextWatcher personalizzato per ogni dominio.

risposta

1

Il mio approccio è di ritardare il metodo notifyPropertyChanged che chiama usando Handles. In questo modo mentre l'utente sta scrivendo, il codice non viene eseguito, quindi 2,5 secondi dopo che l'utente si è fermato per digitare l'ultimo carattere, verrà chiamato notificationPropertyChanged.

L'effetto visivo è interessante e l'utente è libero di scrivere numeri come desidera.

vedere questi due esempi:

Usa può utilizzare questo codice compatto per ogni 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); 
} 

Questo codice invece è utile quando si utilizza il convertitore di valuta o convertitore per cento. L'utente può scrivere un doppio normale, il codice convertito in stringa di valuta. Se il setter viene chiamato con la stringa di valuta, il codice è in grado di convertirlo anche in doppio.

// 
// 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); 
} 
+0

Grazie! Questo è un esempio utile e realistico e probabilmente non esiste un modo migliore. Tuttavia, quello che cerco davvero è un modo elegante e semplice per legare un float a un campo con un codice minimo. Mi sembra strano che sia così semplice legare una stringa e così complessa da legare un galleggiante. –