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