2012-11-16 8 views
5

Ho migliorato la mia precedente implementazione della convalida TextField, questa volta rendendo un vero controllo personalizzato con convalida in tempo reale utilizzando l'associazione. È utilizzabile con FXML senza la necessità di più codice Java.JavaFX 2.2 FXML Validated TextField

import javafx.beans.binding.BooleanBinding; 
import javafx.beans.property.BooleanProperty; 
import javafx.beans.property.IntegerProperty; 
import javafx.beans.property.ReadOnlyBooleanProperty; 
import javafx.beans.property.ReadOnlyIntegerProperty; 
import javafx.beans.property.ReadOnlyStringProperty; 
import javafx.beans.property.SimpleBooleanProperty; 
import javafx.beans.property.SimpleIntegerProperty; 
import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.property.StringProperty; 
import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 
import javafx.scene.control.TextField; 
import javafx.scene.effect.BlurType; 
import javafx.scene.effect.DropShadow; 
import javafx.scene.effect.Effect; 
import javafx.scene.paint.Color; 

/** 
* <p> 
* TextField with regex-based real-time input validation. 
* JavaFX 2 and FXML compatible. </p> 
* <p> 
* FXML code example:<div> 
* {@code <ValidatedTextField fx:id="validatedTextField" minLength="1" maxLength="1" mask="^[0-9]*$" />} 
* </div> 
* </p> 
* 
* @author 82300009 
*/ 
public final class ValidatedTextField extends TextField { 

    private final BooleanProperty invalid = new SimpleBooleanProperty(false); 
    private final StringProperty mask; 
    private final IntegerProperty minLength; 
    private final IntegerProperty maxLength; 

    private Effect invalidEffect = new DropShadow(BlurType.GAUSSIAN, Color.RED, 4, 0.0, 0, 0); 

    public ValidatedTextField() { 
     super(); 
     this.mask = new SimpleStringProperty("."); 
     this.minLength = new SimpleIntegerProperty(-1); 
     this.maxLength = new SimpleIntegerProperty(-1); 

     bind(); 
    } 

    public ValidatedTextField(String mask, int minLength, int maxLength, boolean nullable) { 
     this(mask, minLength, maxLength, nullable, null); 
    } 

    public ValidatedTextField(String mask, int minLength, int maxLength, boolean nullable, String string) { 
     super(string); 
     this.mask = new SimpleStringProperty(mask); 
     this.minLength = new SimpleIntegerProperty(minLength); 
     this.maxLength = new SimpleIntegerProperty(maxLength); 

     bind(); 
    } 

    public ReadOnlyBooleanProperty invalidProperty() { 
     return invalid; 
    } 

    public ReadOnlyStringProperty maskProperty() { 
     return mask; 
    } 

    public ReadOnlyIntegerProperty minLengthProperty() { 
     return minLength; 
    } 

    public ReadOnlyIntegerProperty maxLengthProperty() { 
     return maxLength; 
    } 

    public boolean getInvalid() { 
     return invalid.get(); 
    } 

    public String getMask() { 
     return mask.get(); 
    } 

    public void setMask(String mask) { 
     this.mask.set(mask); 
    } 

    public int getMinLength() { 
     return minLength.get(); 
    } 

    public void setMinLength(int minLength) { 
     this.minLength.set(minLength); 
    } 

    public int getMaxLength() { 
     return maxLength.get(); 
    } 

    public void setMaxLength(int maxLength) { 
     this.maxLength.set(maxLength); 
    } 

    public Effect getInvalidEffect() { 
     return this.invalidEffect; 
    } 

    public void setInvalidEffect(Effect effect) { 
     this.invalidEffect = effect; 
    } 

    private void bind() { 
     this.invalid.bind(maskCheck().or(minLengthCheck())); 

     this.textProperty().addListener(new ChangeListener<String>() { 
      @Override 
      public void changed(ObservableValue<? extends String> ov, String t, String t1) { 
       if (textProperty().get().length() > maxLength.get()) { 
        setText(t); 
       } 
      } 
     }); 

     this.invalid.addListener(new ChangeListener<Boolean>() { 
      @Override 
      public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) { 
       if (t^t1) { 
        if (t1) { 
//      setStyle("-fx-font-weight: bold; -fx-text-fill: red;"); 
         setEffect(invalidEffect); 
        } else { 
//      setStyle("-fx-font-weight: normal; -fx-text-fill: inherit;"); 
         setEffect(null); 
        } 
       } 

      } 
     }); 
    } 

    private BooleanBinding maskCheck() { 
     return new BooleanBinding() { 
      { 
       super.bind(textProperty(), mask); 
      } 

      @Override 
      protected boolean computeValue() { 
       return !textProperty().get().matches(mask.get()); 
      } 
     }; 
    } 

    private BooleanBinding minLengthCheck() { 
     return new BooleanBinding() { 
      { 
       super.bind(textProperty(), minLength); 
      } 

      @Override 
      protected boolean computeValue() { 
       return textProperty().get().length() < minLength.get(); 
      } 
     }; 
    } 

    private BooleanBinding maxLengthCheck() { 
     return new BooleanBinding() { 
      { 
       super.bind(textProperty(), maxLength); 
      } 

      @Override 
      protected boolean computeValue() { 
       return textProperty().get().length() > maxLength.get(); 
      } 
     }; 
    } 
} 

C'è ancora, tuttavia, un punto banale sull'effetto grafico "non valido". Come si può vedere qui:

this.invalid.addListener(new ChangeListener<Boolean>() { 
      @Override 
      public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) { 
       if (t^t1) { 
        if (t1) { 
//      setStyle("-fx-font-weight: bold; -fx-text-fill: red;"); 
         setEffect(invalidEffect); 
        } else { 
//      setStyle("-fx-font-weight: normal; -fx-text-fill: inherit;"); 
         setEffect(null); 
        } 
       } 

      } 
     }); 

ho provato con la setStyle ma utilizzando -fx-font-weight: ereditare; interrompe il codice (non perché, dovrebbe essere il suo valore predefinito). L'iniezione di una StyleClass non funziona poiché non posso ripristinarla quando non valido è false.

Qualsiasi indizio? È naturalmente possibile staccare l'ascoltatore interno e collegarne un altro esterno con altri effetti (per esempio mostrare un segno di spunta verde invece di alterare l'effetto Campo di testo).

Siete liberi di utilizzare il codice se si mente :)

risposta

3

Si può sempre ritornare uno stile rimuovendolo dalla lista delle classi di stile ie

node.getStyleClass().remove("my-style"); 
+0

Ora sto utilizzando esso, ma sembra piuttosto lento ... inoltre non si applica pseudo-stato: non valido. Non dovrebbe leggere un valore di variabile non valido e applicare lo pseudostato? – matticala

+1

Ho usato la tecnica di stile aggiungi/rimuovi e non l'ho trovato lento, potrebbe essere necessario misurare se questa parte di codice è davvero il problema. È necessario implementare una skin per avere uno stato diverso da quelli forniti. Vedi https://github.com/shemnon/DeckControl/tree/master/src/main/java/com/github/shemnon/deckcontrol/skin per alcuni esempi di codice. –

+0

L'ho indovinato. In effetti sto osservando il tutorial di JavaOne 2011 sulla creazione di controlli personalizzati (http://www.parleys.com/#st=5&id=2789&sl=35) per implementare un controllo ridistribuibile appropriato. Daremo un'occhiata anche a quel github, grazie mille! – matticala

Problemi correlati