2012-11-16 35 views
5

Tôi đã cải thiện việc thực thi TextField trước đây của tôi, lần này tạo một Custom Control đúng với xác nhận hợp lệ thời gian thực bằng cách sử dụng binding. Nó có thể sử dụng được với FXML mà không cần thêm mã 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(); 
      } 
     }; 
    } 
} 

Tuy nhiên, vẫn có một điểm nhỏ về hiệu ứng đồ họa "không hợp lệ". Như bạn có thể thấy ở đây:

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

      } 
     }); 

Tôi đã thử với setStyle nhưng sử dụng -fx-font-weight: kế thừa; ngắt mã (không phải vì sao nó phải là giá trị mặc định của nó). Việc tiêm StyleClass không hoạt động vì tôi không thể hoàn nguyên khi không hợp lệ.

Bất kỳ đầu mối nào? Tất nhiên là có thể tách người nghe bên trong và gắn một cái khác ra ngoài với các hiệu ứng khác (f.i. hiển thị một dấu tick màu xanh lá cây thay vì thay đổi hiệu ứng TextField).

Bạn có thể tự do sử dụng mã nếu bạn quan tâm :)

Trả lời

3

Bạn luôn có thể trở lại một phong cách bằng cách loại bỏ nó ra khỏi danh sách các lớp phong cách tức là

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

bây giờ tôi đang sử dụng nó, nhưng nó có vẻ khá chậm ... hơn nữa nó không áp dụng trạng thái giả: không hợp lệ. Không nên đọc giá trị biến không hợp lệ và áp dụng giả mạo? – matticala

+1

Tôi đã sử dụng kỹ thuật thêm/xóa kiểu và không tìm thấy nó chậm, bạn có thể cần phải đo lường nếu phần này của mã thực sự là vấn đề. Bạn cần phải thực hiện một Skin để có một nhà nước khác với những người được cung cấp. Xem https://github.com/shemnon/DeckControl/tree/master/src/main/java/com/github/shemnon/deckcontrol/skin để biết một số ví dụ về mã. –

+0

Tôi đoán vậy. Trong thực tế, bây giờ tôi đang xem hướng dẫn JavaOne 2011 về việc tạo các điều khiển tùy chỉnh (http://www.parleys.com/#st=5&id=2789&sl=35) để thực hiện một điều khiển thích hợp redistributable. Tôi cũng sẽ xem github đó, cảm ơn rất nhiều! – matticala