JavaFX datepicker 不更新值

Posted

技术标签:

【中文标题】JavaFX datepicker 不更新值【英文标题】:JavaFX datepicker not updating value 【发布时间】:2015-11-27 14:12:42 【问题描述】:

有点奇怪,我正在使用 JavaFX 8,并且在我的 Jubula 测试期间发现了一些奇怪的行为。

我有一个使用以下代码创建的日期选择器控件:

public DatePicker getDatePicker(DtDate defaultDate, int width)
    DatePicker dtpckr = new DatePicker();
    dtpckr.setMaxWidth(width);
    dtpckr.setMinWidth(width);
    dtpckr.setConverter(new StringConverter<LocalDate>() 
        private DateTimeFormatter dateTimeFormatter=DateTimeFormatter.ofPattern("yyyy/MM/dd");
        @Override
        public String toString(LocalDate localDate) 
            if(localDate==null)
                return "";
            return dateTimeFormatter.format(localDate);
        
        @Override
        public LocalDate fromString(String dateString) 
            if(dateString==null || dateString.trim().isEmpty())
                return null;
            return LocalDate.parse(dateString,dateTimeFormatter);
        
    );
    dtpckr.setPromptText("yyyy/MM/dd");
    dtpckr.setValue(LocalDate.parse(defaultDate.toString(), DateTimeFormatter.ofPattern("yyyy/MM/dd")));
    return dtpckr;

效果很好,并且控件在表单上创建并按预期工作。如果我使用日历选择日期,则值会更新。

现在,奇怪的是,如果我输入日期选择器并手动输入日期(例如 2017/10/11),然后通过制表符或单击表单上的下一个文本框(日期选择器)退出控件localdate 值不会更新。更新日期的唯一方法是按 Enter 以强制日期选择器实现新值的存在。这会导致另一个问题,因为我有以下代码:

public void createSetDateAndTimeControls()
    DtDateAndTime currentDateAndTime;
    try 
        currentDateAndTime = systemstateController.getServerDateTime();
        /*
         * Creating controls to be used on the form
         */
        int width = 150;
        DatePicker dtpckr = getDatePicker(currentDateAndTime.date, width);
        Label lblDate = new Label("Date:");
        Label lblTimeHour = new Label("Hour:");
        Label lblTimeMinute = new Label("Minute:");
        Label lblTimeSecond = new Label("Second:");
        TextField txtfldCurrentSetSecond = new TextField();
        TextField txtfldCurrentSetMinute = new TextField();
        TextField txtfldCurrentSetHour = new TextField();
        Slider sldrHourPicker = getSlider(width, SliderType.Hour, txtfldCurrentSetHour, currentDateAndTime.time);
        Slider sldrMinutePicker = getSlider(width, SliderType.Minute, txtfldCurrentSetMinute, currentDateAndTime.time);
        Slider sldrSecondPicker = getSlider(width, SliderType.Second, txtfldCurrentSetSecond, currentDateAndTime.time);
        /*
         * Adding a grid pane to keep controls in correct place and aligned correctly
         */
        GridPane grdpn = new GridPane();
        grdpn.add(lblDate, 1, 1);
        grdpn.add(dtpckr, 2, 1, 2,1);
        grdpn.add(lblTimeHour, 1, 2);
        grdpn.add(sldrHourPicker, 2, 2, 2 ,1);
        grdpn.add(txtfldCurrentSetHour, 4, 2);
        grdpn.add(lblTimeMinute, 1, 3);
        grdpn.add(sldrMinutePicker, 2, 3, 2, 1);
        grdpn.add(txtfldCurrentSetMinute, 4, 3);
        grdpn.add(lblTimeSecond, 1, 4);
        grdpn.add(sldrSecondPicker, 2, 4, 2, 1);
        grdpn.add(txtfldCurrentSetSecond, 4, 4);
        /*
         * Creating buttons for user to press
         */
        Button bttnOK = new Button("Change date and time");
        bttnOK.setOnAction(new EventHandler<ActionEvent>() 
            @Override
            public void handle(ActionEvent event) 
                try 
                    if (checkIfAllDialogHasBeenFilledIn(grdpn))
                        LocalDate test = dtpckr.getValue();
                        if (systemstateController.oeSetClock(ICrashUtils.setDateAndTime(dtpckr.getValue().getYear(), dtpckr.getValue().getMonthValue(), dtpckr.getValue().getDayOfMonth(),
                                (int)Math.floor(sldrHourPicker.getValue()), (int)Math.floor(sldrMinutePicker.getValue()), (int)Math.floor(sldrSecondPicker.getValue()))).getValue())
                            showOKMessage("Updated", "Date and time was updated successfully");
                        else
                            showErrorMessage("Error", "There was an error updating the date and time");
                    
                    else
                        showUserCancelledActionPopup();
                 catch (ServerOfflineException | ServerNotBoundException e) 
                    showExceptionErrorMessage(e);
                
            
        );
        bttnOK.setDefaultButton(true);
        grdpn.add(bttnOK, 2, 5, 3, 1);
        Button bttnSetNow = new Button("Now");
        bttnSetNow.setOnAction(new EventHandler<ActionEvent>() 
            @Override
            public void handle(ActionEvent event) 
                dtpckr.setValue(LocalDate.now());
                LocalTime time = LocalTime.now();
                sldrHourPicker.setValue(time.getHour());
                sldrMinutePicker.setValue(time.getMinute());
                sldrSecondPicker.setValue(time.getSecond());
            
        );

        grdpn.add(bttnSetNow, 4, 1);
        /*
         * End of creating controls to be used on the form
         */
        anchrpnActivator.getChildren().add(grdpn);
        AnchorPane.setTopAnchor(grdpn, 0.0);
        AnchorPane.setRightAnchor(grdpn, 0.0);
        AnchorPane.setLeftAnchor(grdpn, 0.0);
        AnchorPane.setBottomAnchor(grdpn, 0.0);
     catch (ServerOfflineException | ServerNotBoundException e) 
        showExceptionErrorMessage(e);
    

其中有以下一行:

bttnOK.setDefaultButton(true);

这意味着如果用户在日期选择器控件上的文本输入过程中按下回车键,它会在用户将数据放入其他控件之前触发 onaction 事件,而我并不特别希望这样.

所以问题是,为什么dtpckr.getValue() 不改变如果用户在框中输入文本然后离开框而不按回车键?我找不到一个事件来调用用户离开日期选择器框或日期选择器的属性来获取控件的文本值并将其与dtpckr.getValue() 的值进行比较。我在这里有什么遗漏吗?

如果您需要更多信息,请告诉我!

[编辑]

It appears this might be a bug in the current datepicker setup in JavaFX

我可能必须删除默认按钮并在 Jubula 测试中按回车键才能使其正常工作。除非有人有替代方案?

【问题讨论】:

【参考方案1】:

The bug log that I added above had the answer。您可以通过以下代码访问文本框的字符串值:

datePicker.getEditor().getText();

因此可以通过以下方式设置文本框值:

datePicker.setValue(datePicker.getConverter()
    .fromString(datePicker.getEditor().getText()));

我正在向丢失焦点事件添加一个事件,这将强制更新 datepicker 值


以及工作代码:

public DatePicker getDatePicker(DtDate defaultDate, int width)
    DatePicker dtpckr = new DatePicker();
    dtpckr.setMaxWidth(width);
    dtpckr.setMinWidth(width);
    dtpckr.setConverter(new StringConverter<LocalDate>() 
        private DateTimeFormatter dateTimeFormatter=DateTimeFormatter.ofPattern("yyyy/MM/dd");
        @Override
        public String toString(LocalDate localDate) 
            if(localDate==null)
                return "";
            return dateTimeFormatter.format(localDate);
        
        @Override
        public LocalDate fromString(String dateString) 
            if(dateString==null || dateString.trim().isEmpty())
                return null;
            try
                return LocalDate.parse(dateString,dateTimeFormatter);
            
            catch(Exception e)
                //Bad date value entered
                return null;
            
        
    );
    dtpckr.setPromptText("yyyy/MM/dd");
    dtpckr.setValue(LocalDate.parse(defaultDate.toString(), DateTimeFormatter.ofPattern("yyyy/MM/dd")));
    //This deals with the bug located here where the datepicker value is not updated on focus lost
    //https://bugs.openjdk.java.net/browse/JDK-8092295?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
    dtpckr.focusedProperty().addListener(new ChangeListener<Boolean>() 
        @Override
        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) 
            if (!newValue)
                dtpckr.setValue(dtpckr.getConverter().fromString(dtpckr.getEditor().getText()));
            
        
    );
    return dtpckr;

【讨论】:

【参考方案2】:

@Draken 的回答需要一些额外的东西。如果用户键入无效日期,代码将抛出 DateTimeParseException。您可以模拟 DatePicker 自己的行为:

dtpckr.getEditor().focusedProperty().addListener((obj, wasFocused, isFocused)->
      if (!isFocused) 
         try 
             dtpckr.setValue(dtpckr.getConverter().fromString(dtpckr.getEditor().getText()));
          catch (DateTimeParseException e) 
                dtpckr.getEditor().setText(dtpckr.getConverter().toString(dtpckr.getValue()));
         
      
);

【讨论】:

【参考方案3】:

我遇到了同样的问题。有没有这个技巧来解决它:

Date date = DateConverter.toDate(datePicker.getEditor().getText());

其中DateConverter.toDate() 是将字符串转换为日期的方法。

【讨论】:

【参考方案4】:

我最近在最新的 JavaFX 版本中修复了这个问题:https://github.com/openjdk/jfx/pull/679

因此,当 JavaFX 18-ea+9 发布时,您可以使用它并删除解决方法。 :-)

【讨论】:

很好地传播错误修复:))【参考方案5】:

我知道这有点离题,但有一段时间我的用户没有按 ENTER 提交,所以我这样做了。

/**
 * This will automatically submit the input on lost of focus, if it's typed but not submitted (with ENTER).
 * 
 * @param picker to apply
 */
private void activateAutoSubmit(DatePicker picker) 
    // On Focus Leave Listener
    picker.focusedProperty().addListener((observable, oldValue, newValue) -> 
        if (!newValue) 
            try 
                // Set typed text to DatePicker value
                picker.setValue(picker.getConverter().fromString(picker.getEditor().getText()));
             catch (Exception e) 
                // For wrong input return old value
                picker.getEditor().setText(picker.getConverter().toString(picker.getValue()));
            
        
    );

用法

@FXML
private DatePicker dateStart;

@FXML
private DatePicker dateEnd;

@FXML
private void initialize() 
    ...
    activateAutoSubmit(dateStart);
    activateAutoSubmit(dateEnd);
    ...

【讨论】:

这已修复.. 并已回归:bugs.openjdk.java.net/browse/JDK-8191995

以上是关于JavaFX datepicker 不更新值的主要内容,如果未能解决你的问题,请参考以下文章

特定格式的 JavaFX DatePicker getValue

动态显示DatePicker

bs datepicker的值更新但显示值不更新

JavaFX 8 DatePicker 风格

JavaFX DatePicker 与时间

JavaFX8 DatePicker 按钮大小更改