2012-11-16 25 views
5

Ich habe meine vorherige Implementierung der TextField-Validierung verbessert und diesmal ein echtes Custom Control mit Echtzeitvalidierung durch Bindung erstellt. Es ist mit FXML verwendbar, ohne dass mehr Java-Code benötigt wird.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(); 
      } 
     }; 
    } 
} 

Es gibt jedoch immer noch einen trivialen Punkt über den "ungültigen" grafischen Effekt. Wie Sie hier sehen können:

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

      } 
     }); 

ich mit dem setStyle versucht, aber mit -fx-font-weight: erben; bricht den Code (nicht warum, denn es sollte sein Standardwert sein). Das Einfügen einer StyleClass funktioniert nicht, da ich sie nicht zurücksetzen kann, wenn false ist.

Irgendwelche Hinweise? Es ist natürlich möglich, den inneren Hörer zu lösen und einen anderen mit anderen Effekten anzuhängen (z. B. ein grünes Häkchen zu zeigen, anstatt den TextField-Effekt zu verändern).

Sie sind frei, den Code verwenden, wenn Sie :)

Antwort

3

dagegen können Sie immer einen Stil zurückkehren, indem sie aus der Liste der Style-Klassen zu entfernen, dh

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

Ich verwende es jetzt, aber es scheint eher langsam ... außerdem gilt es nicht Pseudo-Zustand: ungültig. Sollte es ungültigen Variablenwert lesen und Pseudozustand anwenden? – matticala

+1

Ich habe die Add/Remove-Stil-Technik verwendet und fand es nicht langsam, Sie müssen möglicherweise messen, ob dieser Teil des Codes wirklich das Problem ist. Sie müssen eine Skin implementieren, um einen anderen Status als die angegebenen zu erhalten. Unter https://github.com/shemnon/DeckControl/tree/master/src/main/java/com/github/shemnon/deckcontrol/skin finden Sie einige Codebeispiele. –

+0

Ich vermutete es. In der Tat beobachte ich jetzt das JavaOne 2011-Tutorial zum Erstellen von benutzerdefinierten Steuerelementen (http://www.parleys.com/#st=5&id=2789&sl=35), um eine ordnungsgemäße weitervertreibbare Kontrolle zu implementieren. Ich werde mir auch diesen GitHub ansehen, vielen Dank! – matticala