วิธีที่แนะนำในการสร้าง TextField ที่เป็นตัวเลขใน JavaFX คืออะไร?


85

ฉันต้องการ จำกัด อินพุตใน TextField เป็นจำนวนเต็ม คำแนะนำใด ๆ?


3
หมายเหตุ: ฟังข้อความคุณสมบัติไม่ถูกต้อง ! นั่นคือสิ่งที่ดีที่สุดที่สามารถทำได้ก่อนที่จะมี TextFormatter (ตั้งแต่ fx8u40 หรือมากกว่านั้น) ตั้งแต่นั้นมาจึงใช้กฎพื้นฐาน: โดยทั่วไปถือว่าเป็นการปฏิบัติที่ไม่ดีในการแก้ไขค่าที่สังเกตได้ในวิธีนี้และการใช้TextFormatterเป็นวิธีเดียวที่ยอมรับได้
kleopatra

คำตอบ:


120

เธรดเก่ามาก แต่ดูเหมือนว่าจะดีกว่าและตัดอักขระที่ไม่ใช่ตัวเลขออกหากวาง

// force the field to be numeric only
textField.textProperty().addListener(new ChangeListener<String>() {
    @Override
    public void changed(ObservableValue<? extends String> observable, String oldValue, 
        String newValue) {
        if (!newValue.matches("\\d*")) {
            textField.setText(newValue.replaceAll("[^\\d]", ""));
        }
    }
});

3
ทำงานได้อย่างยอดเยี่ยม โปรดทราบว่าคุณยังสามารถใช้\\D+(หรือเฉยๆ\\D) แทน[^\\d]หากคุณต้องการบันทึกอักขระสองสามตัว
ricky3350

5
ง่ายกว่านั้นคือการตั้งค่าข้อความกลับเป็น oldValue ด้วยวิธีนี้คุณไม่ต้องยุ่งกับการเปลี่ยน regex (ซึ่งไม่ได้ผลสำหรับฉันเลย)
geisterfurz007

@ geisterfurz007 สิ่งนี้ให้ตัวเลือกในการลอกที่ไม่ใช่ตัวเลขออกจากข้อความที่วาง
Evan Knowles

10
ล้าสมัยดูคำตอบ TextFormatter ด้านล่าง
gerardw

3
อาจเป็นไปได้ว่าใช้Integer.parseInt(newValue)และใช้งานtryและcatchตรวจพบข้อผิดพลาดบนNumberFormatException
Pixel

44

อัปเดต เม.ย. 2559

คำตอบนี้สร้างขึ้นเมื่อหลายปีก่อนและคำตอบเดิมส่วนใหญ่ล้าสมัยแล้ว

ตั้งแต่ Java 8u40 Java จึงมีTextFormatterซึ่งมักจะดีที่สุดสำหรับการบังคับใช้อินพุตของรูปแบบเฉพาะเช่นตัวเลขบน JavaFX TextFields:

ดูคำตอบอื่น ๆ สำหรับคำถามนี้ซึ่งกล่าวถึง TextFormatter โดยเฉพาะ


คำตอบเดิม

มีตัวอย่างบางส่วนในส่วนสำคัญนี้ฉันได้ทำซ้ำหนึ่งในตัวอย่างด้านล่าง:

// helper text field subclass which restricts text input to a given range of natural int numbers
// and exposes the current numeric int value of the edit box as a value property.
class IntField extends TextField {
  final private IntegerProperty value;
  final private int minValue;
  final private int maxValue;

  // expose an integer value property for the text field.
  public int  getValue()                 { return value.getValue(); }
  public void setValue(int newValue)     { value.setValue(newValue); }
  public IntegerProperty valueProperty() { return value; }

  IntField(int minValue, int maxValue, int initialValue) {
    if (minValue > maxValue) 
      throw new IllegalArgumentException(
        "IntField min value " + minValue + " greater than max value " + maxValue
      );
    if (maxValue < minValue) 
      throw new IllegalArgumentException(
        "IntField max value " + minValue + " less than min value " + maxValue
      );
    if (!((minValue <= initialValue) && (initialValue <= maxValue))) 
      throw new IllegalArgumentException(
        "IntField initialValue " + initialValue + " not between " + minValue + " and " + maxValue
      );

    // initialize the field values.
    this.minValue = minValue;
    this.maxValue = maxValue;
    value = new SimpleIntegerProperty(initialValue);
    setText(initialValue + "");

    final IntField intField = this;

    // make sure the value property is clamped to the required range
    // and update the field's text to be in sync with the value.
    value.addListener(new ChangeListener<Number>() {
      @Override public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, Number newValue) {
        if (newValue == null) {
          intField.setText("");
        } else {
          if (newValue.intValue() < intField.minValue) {
            value.setValue(intField.minValue);
            return;
          }

          if (newValue.intValue() > intField.maxValue) {
            value.setValue(intField.maxValue);
            return;
          }

          if (newValue.intValue() == 0 && (textProperty().get() == null || "".equals(textProperty().get()))) {
            // no action required, text property is already blank, we don't need to set it to 0.
          } else {
            intField.setText(newValue.toString());
          }
        }
      }
    });

    // restrict key input to numerals.
    this.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
      @Override public void handle(KeyEvent keyEvent) {
        if(intField.minValue<0) {
                if (!"-0123456789".contains(keyEvent.getCharacter())) {
                    keyEvent.consume();
                }
            }
            else {
                if (!"0123456789".contains(keyEvent.getCharacter())) {
                    keyEvent.consume();
                }
            }
      }
    });

    // ensure any entered values lie inside the required range.
    this.textProperty().addListener(new ChangeListener<String>() {
      @Override public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
        if (newValue == null || "".equals(newValue) || (intField.minValue<0 && "-".equals(newValue))) {
          value.setValue(0);
          return;
        }

        final int intValue = Integer.parseInt(newValue);

        if (intField.minValue > intValue || intValue > intField.maxValue) {
          textProperty().setValue(oldValue);
        }

        value.set(Integer.parseInt(textProperty().get()));
      }
    });
  }
}

43

ฉันรู้ว่านี่เป็นกระทู้ที่ค่อนข้างเก่า แต่สำหรับผู้อ่านในอนาคตนี่เป็นอีกวิธีหนึ่งที่ฉันพบว่าใช้งานง่าย:

public class NumberTextField extends TextField
{

    @Override
    public void replaceText(int start, int end, String text)
    {
        if (validate(text))
        {
            super.replaceText(start, end, text);
        }
    }

    @Override
    public void replaceSelection(String text)
    {
        if (validate(text))
        {
            super.replaceSelection(text);
        }
    }

    private boolean validate(String text)
    {
        return text.matches("[0-9]*");
    }
}

แก้ไข: ขอบคุณnone_และSCBoyสำหรับการปรับปรุงที่แนะนำ


1
มีใครรู้บ้างว่า regex ใช้สำหรับเลขฐานสิบ (เช่น 123.456)? ค่าของพารามิเตอร์ฟังก์ชัน validate (text) "text" คืออักขระที่แก้ไขล่าสุดโดยผู้ใช้ไม่ใช่ทั้งสตริงในช่องข้อความดังนั้น regex อาจไม่ตรงกันอย่างถูกต้อง ตัวอย่างเช่นฉันใช้คำสั่งนี้เช่นนี้: text.matches("\\d+"); และฉันไม่สามารถลบอักขระใด ๆ ในฟิลด์ข้อความได้
mils

1
@mils ดูที่นี่และที่นี่
vellotis

1
ฉันคิดว่านี่เป็นทางออกที่ดี แต่ฉันจะเปลี่ยนสิ่งหนึ่ง แทนที่จะเขียน text.matches ("[0-9] *") ฉันจะสร้างตัวแปรสำหรับรูปแบบและรวบรวมมัน ในรูปแบบโค้ดของคุณจะถูกคอมไพล์ทุกครั้งเมื่อมีคนเขียนอักขระในฟิลด์ซึ่งเป็นค่าใช้จ่ายสำหรับ CPU และหน่วยความจำ ดังนั้นฉันจะเพิ่มตัวแปร: private static Pattern integerPattern = Pattern.compile ("[0-9] *"); และแทนที่ validate body ด้วย: integerPattern.matcher (text) .matches ();
P. Jowko

38

เริ่มต้นด้วย JavaFX 8u40 คุณสามารถตั้งค่าวัตถุ TextFormatter บนช่องข้อความ:

UnaryOperator<Change> filter = change -> {
    String text = change.getText();

    if (text.matches("[0-9]*")) {
        return change;
    }

    return null;
};
TextFormatter<String> textFormatter = new TextFormatter<>(filter);
fieldNport = new TextField();
fieldNport.setTextFormatter(textFormatter);

สิ่งนี้จะหลีกเลี่ยงทั้งการคลาสย่อยและเหตุการณ์การเปลี่ยนแปลงที่ซ้ำกันซึ่งคุณจะได้รับเมื่อคุณเพิ่มตัวฟังการเปลี่ยนแปลงลงในคุณสมบัติข้อความและแก้ไขข้อความในตัวฟังนั้น


30

TextInputมีTextFormatterซึ่งสามารถนำมาใช้ในรูปแบบแปลงและ จำกัด ประเภทของข้อความที่สามารถป้อนข้อมูล

TextFormatterมีตัวกรองที่สามารถใช้ในการปฏิเสธการใส่ เราจำเป็นต้องตั้งค่านี้เพื่อปฏิเสธสิ่งที่ไม่ใช่จำนวนเต็มที่ถูกต้อง นอกจากนี้ยังมีตัวแปลงที่เราต้องตั้งค่าเพื่อแปลงค่าสตริงเป็นค่าจำนวนเต็มซึ่งเราสามารถผูกได้ในภายหลัง

ให้สร้างตัวกรองที่ใช้ซ้ำได้:

public class IntegerFilter implements UnaryOperator<TextFormatter.Change> {
    private final static Pattern DIGIT_PATTERN = Pattern.compile("\\d*");

    @Override
    public Change apply(TextFormatter.Change aT) {
        return DIGIT_PATTERN.matcher(aT.getText()).matches() ? aT : null;
    }
}

ตัวกรองสามารถทำสิ่งใดสิ่งหนึ่งในสามสิ่งโดยสามารถคืนค่าการเปลี่ยนแปลงที่ไม่ได้แก้ไขเพื่อยอมรับตามที่เป็นอยู่สามารถแก้ไขการเปลี่ยนแปลงในลักษณะที่เห็นว่าเหมาะสมหรือสามารถคืนค่าได้ nullไปปฏิเสธการเปลี่ยนแปลงทั้งหมดด้วยกัน

เราจะใช้มาตรฐาน IntegerStringConverterเป็นตัวแปลง

เรามี:

TextField textField = ...;

TextFormatter<Integer> formatter = new TextFormatter<>(
    new IntegerStringConverter(), // Standard converter form JavaFX
    defaultValue, 
    new IntegerFilter());
formatter.valueProperty().bindBidirectional(myIntegerProperty);

textField.setTextFormatter(formatter);

หากคุณไม่ต้องการฟิลเตอร์ที่ใช้ซ้ำได้คุณสามารถทำซับเดียวแบบนี้แทนได้:

TextFormatter<Integer> formatter = new TextFormatter<>(
    new IntegerStringConverter(), 
    defaultValue,  
    c -> Pattern.matches("\\d*", c.getText()) ? c : null );

21

ฉันไม่ชอบข้อยกเว้นดังนั้นฉันจึงใช้matchesฟังก์ชันจาก String-Class

text.textProperty().addListener(new ChangeListener<String>() {
    @Override
    public void changed(ObservableValue<? extends String> observable, String oldValue, 
        String newValue) {
        if (newValue.matches("\\d*")) {
            int value = Integer.parseInt(newValue);
        } else {
            text.setText(oldValue);
        }
    }
});

1
บางครั้งคุณมีข้อยกเว้นเนื่องจากนูเบอร์อาจมีขนาดใหญ่เกินไป
schlagi123

1
เคล็ดลับ: แทนที่ regex "\\ d +" เป็น "\\ d *" ซึ่งจะช่วยให้คุณสามารถลบอักขระทั้งหมดออกจากช่องป้อนข้อความ (ใช้ backspace / delete เพื่อล้างฟิลด์)
Guus

1
อย่าลืมตั้งค่าตำแหน่งtextField.positionCaret(textField.getLength());
คาเร็ต

เปลี่ยนเงื่อนไข fist if เป็น: if (newValue.matches("\\d*") && newValue.getText().length < 5) หากคุณต้องการ จำกัด อินพุตเป็น 4 หลักในกรณีนี้
Cappuccino90

@ Cappuccino90 คุณควรเปลี่ยนงบเพราะการตรวจสอบความยาวจะถูกกว่ามากในแต่ละ Hit มากกว่าการแยกวิเคราะห์ regex
thatsIch

9

เริ่มจากJava SE 8u40สำหรับความต้องการดังกล่าวคุณสามารถใช้ " จำนวนเต็ม "Spinnerเพื่อให้สามารถเลือกจำนวนเต็มที่ถูกต้องได้อย่างปลอดภัยโดยใช้ปุ่มลูกศรขึ้น / ลูกศรลงของแป้นพิมพ์หรือปุ่มลูกศรขึ้น / ลงที่มีให้

นอกจากนี้คุณยังสามารถกำหนดนาทีที่สูงสุดและเริ่มต้นความคุ้มค่าที่จะ จำกัด ค่าได้รับอนุญาตและจำนวนเงินที่จะเพิ่มขึ้นหรือลดลงโดยต่อขั้นตอน

ตัวอย่างเช่น

// Creates an integer spinner with 1 as min, 10 as max and 2 as initial value
Spinner<Integer> spinner1 = new Spinner<>(1, 10, 2);
// Creates an integer spinner with 0 as min, 100 as max and 10 as initial 
// value and 10 as amount to increment or decrement by, per step
Spinner<Integer> spinner2 = new Spinner<>(0, 100, 10, 10);

ตัวอย่างผลลัพธ์ที่มี " จำนวนเต็มปินเนอร์ " " และสปินเนอร์ " คู่ "

enter image description here

ปินเนอร์คือตัวควบคุมฟิลด์ข้อความบรรทัดเดียวที่ให้ผู้ใช้เลือกตัวเลขหรือค่าอ็อบเจ็กต์จากลำดับของค่าดังกล่าว โดยทั่วไปแล้ว Spinners จะมีปุ่มลูกศรเล็ก ๆ คู่หนึ่งสำหรับก้าวผ่านองค์ประกอบต่างๆของลำดับ แป้นลูกศรขึ้น / ลงของแป้นพิมพ์ยังวนรอบองค์ประกอบต่างๆ ผู้ใช้อาจได้รับอนุญาตให้พิมพ์ค่า (ถูกกฎหมาย) ลงในสปินเนอร์โดยตรง แม้ว่ากล่องคำสั่งผสมจะมีฟังก์ชันการทำงานที่คล้ายคลึงกัน แต่บางครั้งตัวหมุนก็เป็นที่ต้องการเนื่องจากไม่ต้องการรายการแบบเลื่อนลงที่สามารถปิดบังข้อมูลที่สำคัญได้และเนื่องจากอนุญาตให้ใช้คุณลักษณะต่างๆเช่นการตัดจากค่าสูงสุดกลับไปเป็นค่าต่ำสุด (เช่น จากจำนวนเต็มบวกมากที่สุดถึง 0)

รายละเอียดเพิ่มเติมเกี่ยวกับการควบคุม Spinner


ผู้คนไม่ควรสปินเนอร์ไม่ตรวจสอบการป้อนข้อความหรือสูญเสียโฟกัส
geometrikal

7

คำตอบที่เตรียมไว้อาจมีขนาดเล็กลงหากคุณใช้ Java 1.8 Lambdas

textfield.textProperty().addListener((observable, oldValue, newValue) -> {
    if (newValue.matches("\\d*")) return;
    textfield.setText(newValue.replaceAll("[^\\d]", ""));
});

6
TextField text = new TextField();

text.textProperty().addListener(new ChangeListener<String>() {
    @Override
    public void changed(ObservableValue<? extends String> observable,
            String oldValue, String newValue) {
        try {
            Integer.parseInt(newValue);
            if (newValue.endsWith("f") || newValue.endsWith("d")) {
                manualPriceInput.setText(newValue.substring(0, newValue.length()-1));
            }
        } catch (ParseException e) {
            text.setText(oldValue);
        }
    }
});

ifข้อเป็นสิ่งที่สำคัญต่อปัจจัยการผลิตเช่นจับ 0.5d หรือ 0.7f ซึ่งมีการแยกวิเคราะห์อย่างถูกต้องโดย Int.parseInt () แต่ไม่ควรจะปรากฏในช่องข้อความ


9
ว๊อท? ตั้งแต่เมื่อใดที่ 0.5d ถูกแยกวิเคราะห์อย่างถูกต้องโดย Integer.parseInt () ?! มันพ่น java.lang.NumberFormatException: สำหรับสตริงอินพุต: "0.5d" (ตามที่คาดไว้เนื่องจากไม่ใช่จำนวนเต็ม)
Burkhard

4

ลองใช้รหัสง่ายๆนี้มันจะทำงานได้

DecimalFormat format = new DecimalFormat( "#.0" );
TextField field = new TextField();
field.setTextFormatter( new TextFormatter<>(c ->
{
    if ( c.getControlNewText().isEmpty() )
    {
        return c;
    }

    ParsePosition parsePosition = new ParsePosition( 0 );
    Object object = format.parse( c.getControlNewText(), parsePosition );

    if ( object == null || parsePosition.getIndex() <          c.getControlNewText().length() )
    {
        return null;
    }
    else
    {
        return c;
    }
}));

3

หากคุณต้องการใช้ผู้ฟังเดียวกันกับ TextField มากกว่าหนึ่งรายการนี่คือวิธีที่ง่ายที่สุด:

TextField txtMinPrice, txtMaxPrice = new TextField();

ChangeListener<String> forceNumberListener = (observable, oldValue, newValue) -> {
    if (!newValue.matches("\\d*"))
      ((StringProperty) observable).set(oldValue);
};

txtMinPrice.textProperty().addListener(forceNumberListener);
txtMaxPrice.textProperty().addListener(forceNumberListener);

3

อันนี้ใช้ได้ผลสำหรับฉัน

public void RestrictNumbersOnly(TextField tf){
    tf.textProperty().addListener(new ChangeListener<String>() {
        @Override
        public void changed(ObservableValue<? extends String> observable, String oldValue, 
            String newValue) {
            if (!newValue.matches("|[-\\+]?|[-\\+]?\\d+\\.?|[-\\+]?\\d+\\.?\\d+")){
                tf.setText(oldValue);
            }
        }
    });
}

3

ฉันต้องการความช่วยเหลือจากการรวมคำตอบของ Evan Knowles TextFormatterจาก JavaFX 8

textField.setTextFormatter(new TextFormatter<>(c -> {
    if (!c.getControlNewText().matches("\\d*")) 
        return null;
    else
        return c;
    }
));

ขอให้โชคดี;) ใจเย็น ๆ และเขียนโค้ดจาวา


2

นี่คือคลาสง่ายๆที่จัดการการตรวจสอบความถูกต้องพื้นฐานTextFieldโดยใช้TextFormatterแนะนำใน JavaFX 8u40

แก้ไข:

(รหัสเพิ่มเกี่ยวกับความคิดเห็นของ Floern)

import java.text.DecimalFormatSymbols;
import java.util.regex.Pattern;

import javafx.beans.NamedArg;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;

public class TextFieldValidator {

    private static final String CURRENCY_SYMBOL   = DecimalFormatSymbols.getInstance().getCurrencySymbol();
    private static final char   DECIMAL_SEPARATOR = DecimalFormatSymbols.getInstance().getDecimalSeparator();

    private final Pattern       INPUT_PATTERN;

    public TextFieldValidator(@NamedArg("modus") ValidationModus modus, @NamedArg("countOf") int countOf) {
        this(modus.createPattern(countOf));
    }

    public TextFieldValidator(@NamedArg("regex") String regex) {
        this(Pattern.compile(regex));
    }

    public TextFieldValidator(Pattern inputPattern) {
        INPUT_PATTERN = inputPattern;
    }

    public static TextFieldValidator maxFractionDigits(int countOf) {
        return new TextFieldValidator(maxFractionPattern(countOf));
    }

    public static TextFieldValidator maxIntegers(int countOf) {
        return new TextFieldValidator(maxIntegerPattern(countOf));
    }

    public static TextFieldValidator integersOnly() {
        return new TextFieldValidator(integersOnlyPattern());
    }

    public TextFormatter<Object> getFormatter() {
        return new TextFormatter<>(this::validateChange);
    }

    private Change validateChange(Change c) {
        if (validate(c.getControlNewText())) {
            return c;
        }
        return null;
    }

    public boolean validate(String input) {
        return INPUT_PATTERN.matcher(input).matches();
    }

    private static Pattern maxFractionPattern(int countOf) {
        return Pattern.compile("\\d*(\\" + DECIMAL_SEPARATOR + "\\d{0," + countOf + "})?");
    }

    private static Pattern maxCurrencyFractionPattern(int countOf) {
        return Pattern.compile("^\\" + CURRENCY_SYMBOL + "?\\s?\\d*(\\" + DECIMAL_SEPARATOR + "\\d{0," + countOf + "})?\\s?\\" +
                CURRENCY_SYMBOL + "?");
    }

    private static Pattern maxIntegerPattern(int countOf) {
        return Pattern.compile("\\d{0," + countOf + "}");
    }

    private static Pattern integersOnlyPattern() {
        return Pattern.compile("\\d*");
    }

    public enum ValidationModus {

        MAX_CURRENCY_FRACTION_DIGITS {
            @Override
            public Pattern createPattern(int countOf) {
                return maxCurrencyFractionPattern(countOf);
            }
        },

        MAX_FRACTION_DIGITS {
            @Override
            public Pattern createPattern(int countOf) {
                return maxFractionPattern(countOf);
            }
        },
        MAX_INTEGERS {
            @Override
            public Pattern createPattern(int countOf) {
                return maxIntegerPattern(countOf);
            }
        },

        INTEGERS_ONLY {
            @Override
            public Pattern createPattern(int countOf) {
                return integersOnlyPattern();
            }
        };

        public abstract Pattern createPattern(int countOf);
    }

}

คุณสามารถใช้งานได้ดังนี้:

textField.setTextFormatter(new TextFieldValidator(ValidationModus.INTEGERS_ONLY).getFormatter());

หรือคุณสามารถสร้างอินสแตนซ์ได้ในไฟล์ fxml และนำไปใช้กับ customTextField ด้วยคุณสมบัติตาม

app.fxml:

<fx:define>
    <TextFieldValidator fx:id="validator" modus="INTEGERS_ONLY"/>
</fx:define>

CustomTextField.class:

public class CustomTextField {

private TextField textField;

public CustomTextField(@NamedArg("validator") TextFieldValidator validator) {
        this();
        textField.setTextFormatter(validator.getFormatter());
    }
}

รหัสบน github


2

นี่คือสิ่งที่ฉันใช้:

private TextField textField;
textField.textProperty().addListener(new ChangeListener<String>() {
    @Override
    public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
        if(!newValue.matches("[0-9]*")){
            textField.setText(oldValue);
        }

    }
});

สิ่งเดียวกันในสัญกรณ์แลมบ์ดาจะเป็น:

private TextField textField;
textField.textProperty().addListener((observable, oldValue, newValue) -> {
    if(!newValue.matches("[0-9]*")){
        textField.setText(oldValue);
    }
});

2

วิธีนี้ช่วยให้ TextField เสร็จสิ้นการประมวลผลทั้งหมด (คัดลอก / วาง / เลิกทำปลอดภัย) ไม่จำเป็นต้องขยายคลาสและช่วยให้คุณตัดสินใจว่าจะทำอย่างไรกับข้อความใหม่หลังจากการเปลี่ยนแปลงทุกครั้ง (เพื่อผลักดันให้เป็นตรรกะหรือเปลี่ยนกลับไปเป็นค่าก่อนหน้าหรือแม้กระทั่งแก้ไข)

  // fired by every text property change
textField.textProperty().addListener(
  (observable, oldValue, newValue) -> {
    // Your validation rules, anything you like
      // (! note 1 !) make sure that empty string (newValue.equals("")) 
      //   or initial text is always valid
      //   to prevent inifinity cycle
    // do whatever you want with newValue

    // If newValue is not valid for your rules
    ((StringProperty)observable).setValue(oldValue);
      // (! note 2 !) do not bind textProperty (textProperty().bind(someProperty))
      //   to anything in your code.  TextProperty implementation
      //   of StringProperty in TextFieldControl
      //   will throw RuntimeException in this case on setValue(string) call.
      //   Or catch and handle this exception.

    // If you want to change something in text
      // When it is valid for you with some changes that can be automated.
      // For example change it to upper case
    ((StringProperty)observable).setValue(newValue.toUpperCase());
  }
);

สำหรับกรณีของคุณเพียงแค่เพิ่มตรรกะนี้ภายใน ทำงานได้อย่างสมบูรณ์

   if (newValue.equals("")) return; 
   try {
     Integer i = Integer.valueOf(newValue);
     // do what you want with this i
   } catch (Exception e) {
     ((StringProperty)observable).setValue(oldValue);
   }

0

อืมมม ฉันพบปัญหานั้นเมื่อหลายสัปดาห์ก่อน เนื่องจาก API ไม่ได้ให้การควบคุมเพื่อให้บรรลุเป้าหมายดังกล่าว
คุณอาจต้องการใช้API ของคุณเอง
ฉันใช้สิ่งที่ชอบ:

public class IntegerBox extends TextBox {
    public-init var value : Integer = 0;
    protected function apply() {
        try {
            value = Integer.parseInt(text);
        } catch (e : NumberFormatException) {}
        text = "{value}";
    }
    override var focused = false on replace {apply()};
    override var action = function () {apply()}
}

โดยจะใช้วิธีเดียวกับที่ปกติTextBox,
แต่ยังมีvalueแอตทริบิวต์ที่เก็บจำนวนเต็มป้อน
เมื่อตัวควบคุมสูญเสียโฟกัสตัวควบคุมจะตรวจสอบความถูกต้องของค่าและเปลี่ยนกลับ (ถ้าไม่ถูกต้อง)


2
นี่คือภาษาอะไร?
ชิงทรัพย์รับบี

มันJavaFX Scriptครูบาก็ล้าสมัย
jewelsea

0

รหัสนี้ทำให้ฟิลด์ข้อความของคุณยอมรับเฉพาะตัวเลข

textField.lengthProperty().addListener((observable, oldValue, newValue) -> {
        if(newValue.intValue() > oldValue.intValue()){
            char c = textField.getText().charAt(oldValue.intValue());
            /** Check if the new character is the number or other's */
            if( c > '9' || c < '0'){
                /** if it's not number then just setText to previous one */
                textField.setText(textField.getText().substring(0,textField.getText().length()-1));
            }
        }
    });

0

รหัสนี้ใช้ได้ดีสำหรับฉันแม้ว่าคุณจะพยายามคัดลอก / วาง

myTextField.textProperty().addListener((observable, oldValue, newValue) -> {
    if (!newValue.matches("\\d*")) {
        myTextField.setText(oldValue);

    }
});

-1

ในการอัปเดตล่าสุดของ JavaFX คุณต้องตั้งค่าข้อความใหม่ในเมธอด Platform.runLater เช่นนี้:

    private void set_normal_number(TextField textField, String oldValue, String newValue) {
    try {
        int p = textField.getCaretPosition();
        if (!newValue.matches("\\d*")) {
            Platform.runLater(() -> {
                textField.setText(newValue.replaceAll("[^\\d]", ""));
                textField.positionCaret(p);
            });
        }
    } catch (Exception e) {
    }
}

เป็นความคิดที่ดีที่จะตั้งตำแหน่งคาเร็ตด้วย


1
ขอบคุณสำหรับคำตอบ! คุณช่วยอธิบายหน่อยPlatform.runLaterได้ไหมว่าทำไมถึงจำเป็น?
TuringTux

@TuringTux ใน JavaFX เวอร์ชันล่าสุดจะมีข้อยกเว้นหากคุณไม่ใช้ Platform.runLater
bdshahab

-1

ฉันต้องการปรับปรุงคำตอบของ Evan Knowles: https://stackoverflow.com/a/30796829/2628125

ในกรณีของฉันฉันมีคลาสที่มีตัวจัดการสำหรับส่วน UI Component การเริ่มต้น:

this.dataText.textProperty().addListener((observable, oldValue, newValue) -> this.numericSanitization(observable, oldValue, newValue));

และวิธี numbericSanitization:

private synchronized void numericSanitization(ObservableValue<? extends String> observable, String oldValue, String newValue) {
    final String allowedPattern = "\\d*";

    if (!newValue.matches(allowedPattern)) {
        this.dataText.setText(oldValue);
    }
}

คีย์เวิร์ดซิงโครไนซ์เพื่อป้องกันปัญหาการล็อคการแสดงผลที่อาจเกิดขึ้นใน javafx หาก setText จะถูกเรียกก่อนที่จะดำเนินการเสร็จสิ้น เป็นเรื่องง่ายที่จะทำซ้ำหากคุณจะเริ่มพิมพ์ตัวอักษรผิดเร็วมาก

ข้อดีอีกอย่างคือคุณเก็บเพียงรูปแบบเดียวที่จะจับคู่และทำการย้อนกลับ จะดีกว่าเพราะคุณสามารถแยกสารละลายสำหรับรูปแบบการฆ่าเชื้อที่แตกต่างกันได้อย่างง่ายดาย


-1
rate_text.textProperty().addListener(new ChangeListener<String>() {

    @Override
    public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
        String s="";
        for(char c : newValue.toCharArray()){
            if(((int)c >= 48 && (int)c <= 57 || (int)c == 46)){
                s+=c;
            }
        }
        rate_text.setText(s);
    }
});

ใช้งานได้ดีเนื่องจากอนุญาตให้คุณป้อนเฉพาะค่าจำนวนเต็มและค่าทศนิยม (มีรหัส ASCII 46)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.