Javafx 使用TextFormatter时无法使用日期选择器选择框

Javafx 使用TextFormatter时无法使用日期选择器选择框,javafx,datepicker,Javafx,Datepicker,我想创建三(3)组组合框(年、月、日)。 只有在正确归档组合框月份和年份之前,才应启用组合框日期,并且应根据给定的月份和年份同步值。(这意味着它应该检查闰年) 到目前为止,我有一个提示,我应该使用绑定和/或侦听器来实现这一点,但我很难做到这一点 public class Testing extends Application { public static void main(String[] args) { launch(args); } @Over

我想创建三(3)组组合框(年、月、日)。 只有在正确归档组合框月份和年份之前,才应启用组合框日期,并且应根据给定的月份和年份同步值。(这意味着它应该检查闰年)

到目前为止,我有一个提示,我应该使用绑定和/或侦听器来实现这一点,但我很难做到这一点

public class Testing extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        final JFXComboBox<Month> cbMonths = new JFXComboBox<>();
        final JFXComboBox<Integer> cbYears = new JFXComboBox<>();
        final JFXComboBox<Integer> cbDays = new JFXComboBox<>();

        // Month Values
        cbMonths.getItems().setAll(Month.values());
        // Year Values
        Calendar calendar = Calendar.getInstance();
        for (int i = calendar.get(Calendar.YEAR) ;
             i >= (calendar.get(Calendar.YEAR) -35) ; i--)
        {
            cbYears.getItems().add(i);
        }

        // NOTE: will cause NPE
        //       I want to insert this code only when cbMonth and cbYears has a value
        YearMonth numberOfDays = YearMonth.of(cbYear.getValue(), cbMonth.getValue());
        for (int i = 1 ; i >= numberOfDays.lengthOfMonth() ; i ++) {
            cbDays.getItems().add(i);
        }

        final HBox root = new HBox(cbMonth, cbYear, cbDays);
        root.setAlignment(Pos.CENTER);
        root.setSpacing(10.0);
        root.setPadding(new Insets(10, 10, 10, 10));
        Scene scene = new Scene(root, 300, 200);
        primaryStage.setScene(scene);
        primaryStage.show();

    }

除了可编辑之外,我还希望用户能够单击和/或从选择框中选择日期。我希望有人能给我指出正确的方向:)

在@kleopatra的帮助下。我的解决方案是创建一个类,负责解析用户在DatePickers默认选择框中选择的日期。此外,日期选择器设置为可编辑,以便用户也可以手动编辑它。但是,有一个限制,用户在手动编辑时只能插入数值,而且我想确保用户只输入有效日期


MCVE
公共类DatePickerFinal扩展应用程序{
公共静态void main(字符串[]args){
发射(args);
}
@凌驾
公共无效开始(阶段primaryStage){
最终字符串日期\u出生\u正则表达式
=“(0[1-9]|1[012])\\s(0[1-9]|[12][0-9]|3[01])\\s((19[2[0-9])[0-9]{2});
final DateTimeFormatter showingDateFormat=DateTimeFormatter.of模式(“MMMM dd,yyyy”,Locale.getDefault());
final DateTimeFormatter InputedDateFormat=DateTimeFormatter.of模式(“MM dd yyyy”,Locale.getDefault());
final LocalDate dateToday=LocalDate.now();
final JFXDatePicker datePicker=新JFXDatePicker();
//禁用某些日期
datePicker.setDayCellFactory(新回调(){
@凌驾
公共DateCell调用(日期选择器DatePicker){
返回新的DateCell(){
@凌驾
public void updateItem(LocalDate LocalDate,布尔值b){
super.updateItem(localDate,b);
setDisable(b | | localDate.compareTo(dateToday)>0 | | localDate.compareTo(dateToday.minusYears(45))<0);
}
};
}
});
//添加StringConverter以使其更具可读性,
//以及拒绝用户输入的禁用日期
datePicker.setConverter(新的StringConverter(){
@凌驾
公共字符串toString(LocalDate LocalDate){
如果(localDate==null){
返回“”;
}else if(localDate.isAfter(dateToday)| localDate.isBefore(dateToday.minusYears(45))){
返回“”;
}否则{
返回showingDateFormat.format(localDate);
}
}
@凌驾
public LocalDate fromString(字符串s){
返回(s==null | | s.isEmpty())?null:LocalDate.parse(s,inputedDateFormat);
}
});
//添加一个验证器
RequiredFieldValidator RequiredFieldValidator=新RequiredFieldValidator();
requiredFieldValidator.setMessage(“输入格式为\nMM DD YYYY”);
datePicker.setValidators(requiredFieldValidator);
//格式化用户的输入字段
datePicker.getEditor().setTextFormatter(新的TextFormatter(更改->{
String textEntered=change.getText();
日期确认器;
if(change.isContentChange()){
validator=新的DateValidator(change.getControlNewText(),showingDateFormat);
如果(!validator.isValid()){
datePicker.validate();
}否则{
datePicker.resetValidation();
回报变化;
}
如果(文本输入。匹配(\\D+)){
返回null;
}否则{
final int oldLength=change.getControlText().length();
int newLength=change.getControlNewText().length();
if(newLength{
datePicker.validate();
datePicker.getEditor().selectAll();
});
}否则{
datePicker.resetValidation();
}
});
datePicker.getEditor().textProperty().addListener(新的ChangeListener()){
@凌驾

public void changed(observevaluea)我有一个提示,我应该使用绑定和/或监听器好的方向:)现在应用它,从小处开始(f.I.有两个组合),并在工作时进行扩展-当卡住时,回来演示问题以及它如何不按预期工作b)不要将旧的(日历)和新的日期/时间(年/月)混合使用Api您可能做了太多的工作,JavaFX还包括一个
日期选择器
控件,它可能对您有所帮助。请遵守java命名约定
public class DatePickerFinal extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        final String DATE_REGEX = "(0[1-9]|1[012])\\s(0[1-9]|[12][0-9]|3[01])\\s((19|2[0-9])[0-9]{2})";
        final DateTimeFormatter SHOW_DATE = DateTimeFormatter.ofPattern("MMMM dd, yyyy", Locale.getDefault());
        final DateTimeFormatter ENTER_DATE = DateTimeFormatter.ofPattern("MM dd yyyy", Locale.getDefault());
        final LocalDate TODAY = LocalDate.now();

        final JFXDatePicker DATE_PICKER = new JFXDatePicker();

        // Disable some dates
        DATE_PICKER.setDayCellFactory(new Callback<DatePicker, DateCell>() {
            @Override
            public DateCell call(DatePicker datePicker) {
                return new DateCell() {
                    @Override
                    public void updateItem(LocalDate localDate, boolean b) {
                        super.updateItem(localDate, b);
                        setDisable(b || localDate.compareTo(TODAY) > 0 || localDate.compareTo(TODAY.minusYears(45)) < 0);
                    }
                };
            }
        });

        // Add StringConverter to make it more readable,
        // and also rejecting disable dates inputted by the user
        DATE_PICKER.setConverter(new StringConverter<LocalDate>() {
            @Override
            public String toString(LocalDate localDate) {
                if (localDate == null) {
                    return "";
                } else if (localDate.isAfter(TODAY) || localDate.isBefore(TODAY.minusYears(45))) {
                    return "";
                } else {
                    return SHOW_DATE.format(localDate);
                }
            }

            @Override
            public LocalDate fromString(String s) {
                return (s == null || s.isEmpty()) ? null : LocalDate.parse(s, ENTER_DATE);
            }
        });


        // Then I want to manage user input so that they can only enter digits to the date picker
        // then format it accordingly.

        DATE_PICKER.getEditor().setTextFormatter(new TextFormatter<Object>(change -> {
            String enteredText = change.getText();
            if((enteredText.matches("[\\d]+")) || change.isDeleted()) {
                final int oldTextLength = change.getControlText().length();
                int newTextLength = change.getControlNewText().length();

                if (newTextLength < oldTextLength) return change;
                switch (newTextLength) {
                    case 2 :
                    case 5 :
                        StringBuilder stringBuilder = new StringBuilder(enteredText);
                        stringBuilder.append(" ");
                        change.setText(stringBuilder.toString());
                        newTextLength++;
                        break;
                    case 11 :
                        return null;
                }
                change.setCaretPosition(newTextLength);
                change.setAnchor(newTextLength);
                return change;
            }
            return null;
        }));

        
        // Add some validators where if the user input was valid or not. The below code was still in progress though.
        RequiredFieldValidator requiredFieldValidator = new RequiredFieldValidator();
        requiredFieldValidator.setMessage("Field Should Not Be Empty");
        RegexValidator regexValidator = new RegexValidator("MM DD YYYY");
        regexValidator.setRegexPattern(DATE_REGEX);
        DATE_PICKER.setValidators(regexValidator);
        DATE_PICKER.focusedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observableValue, Boolean aBoolean, Boolean t1) {
                if (t1) {
                    DATE_PICKER.validate();
                }
            }
        });


        DATE_PICKER.getEditor().textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observableValue, String s, String t1) {
                if (!DATE_PICKER.getEditor().getText().matches(DATE_REGEX)) {
                    DATE_PICKER.validate();
                }
            }
        });


        VBox root = new VBox(20, DATE_PICKER, new JFXButton("Button"));
        root.setAlignment(Pos.CENTER);
        Scene scene = new Scene(root, 300, 120);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}
public class DatePickerFinal extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        final String DATE_OF_BIRTH_REGEX
                = "(0[1-9]|1[012])\\s(0[1-9]|[12][0-9]|3[01])\\s((19|2[0-9])[0-9]{2})";
        final DateTimeFormatter showingDateFormat = DateTimeFormatter.ofPattern("MMMM dd, yyyy", Locale.getDefault());
        final DateTimeFormatter inputtedDateFormat = DateTimeFormatter.ofPattern("MM dd yyyy", Locale.getDefault());
        final LocalDate dateToday = LocalDate.now();

        final JFXDatePicker datePicker = new JFXDatePicker();

        // Disable some dates
        datePicker.setDayCellFactory(new Callback<DatePicker, DateCell>() {
            @Override
            public DateCell call(DatePicker datePicker) {
                return new DateCell() {
                    @Override
                    public void updateItem(LocalDate localDate, boolean b) {
                        super.updateItem(localDate, b);
                        setDisable(b || localDate.compareTo(dateToday) > 0 || localDate.compareTo(dateToday.minusYears(45)) < 0);
                    }
                };
            }
        });

        // Add StringConverter to make it more readable,
        // and also rejecting disable dates inputted by the user
        datePicker.setConverter(new StringConverter<LocalDate>() {
            @Override
            public String toString(LocalDate localDate) {
                if (localDate == null) {
                    return "";
                } else if (localDate.isAfter(dateToday) || localDate.isBefore(dateToday.minusYears(45))) {
                    return "";
                } else {
                    return showingDateFormat.format(localDate);
                }
            }

            @Override
            public LocalDate fromString(String s) {
                return (s == null || s.isEmpty()) ? null : LocalDate.parse(s, inputtedDateFormat);
            }
        });

        // Add a validator
        RequiredFieldValidator requiredFieldValidator = new RequiredFieldValidator();
        requiredFieldValidator.setMessage("Enter with the format\nMM DD YYYY");
        datePicker.setValidators(requiredFieldValidator);

        // Format the user's input field
        datePicker.getEditor().setTextFormatter(new TextFormatter<>(change -> {
            String textEntered = change.getText();

            DateValidator validator;

            if (change.isContentChange()) {

                validator = new DateValidator(change.getControlNewText(), showingDateFormat);
                if (!validator.isValid()) {
                    datePicker.validate();
                } else {
                    datePicker.resetValidation();
                    return change;
                }

                if (textEntered.matches("\\D+")) {
                    return null;
                } else {
                    final int oldLength = change.getControlText().length();
                    int newLength = change.getControlNewText().length();
                    if (newLength < oldLength) return change;

                    if (newLength == 2 || newLength == 5) {
                        change.setText(textEntered + " ");
                        newLength++;
                    } else if (newLength == 11) {
                        validator = new DateValidator(change.getControlNewText(), inputtedDateFormat);
                        if (!validator.isValid()) {
                            return null;
                        } else {
                            datePicker.resetValidation();
                        }
                    }

                    change.setCaretPosition(newLength);
                    change.setAnchor(newLength);
                }
            }

            return change;
        }));

        datePicker.focusedProperty().addListener((observableValue, wasFocused, isFocused) -> {
            if (isFocused) {
                Platform.runLater(()-> {
                    datePicker.validate();
                    datePicker.getEditor().selectAll();
                });
            } else {
                datePicker.resetValidation();
            }
        });

        datePicker.getEditor().textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observableValue, String s, String t1) {
                if (t1.matches(DATE_OF_BIRTH_REGEX)) {
                    datePicker.resetValidation();
                }
            }
        });

        // Show picker choice box on MouseEvent
        datePicker.addEventFilter(MouseEvent.MOUSE_CLICKED, mouseEvent -> {
            datePicker.show();
        });

        VBox root = new VBox(50, datePicker, new JFXButton("Button"));
        root.setAlignment(Pos.CENTER);
        Scene scene = new Scene(root, 300, 120);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private static class DateValidator  {
        DateTimeFormatter formatter;
        String date;

        DateValidator (String date, DateTimeFormatter formatter) {
            this.date = date;
            this.formatter = formatter;
        }

        public boolean isValid() {
            try {
                LocalDate.parse(this.date, this.formatter);
            } catch (Exception e) {
                return false;
            }
            return true;
        }
    }
}