如果在 ChangeListener 中更改焦点,则 DatePicker 不会提交值

Zep*_*hyr 7 java javafx

JDK 1.8 (8u231)、Windows 10 出现以下问题

我有一个DatePicker带有侦听器的设置,该侦听器显示Alert值何时更改。但是,一旦Alert显示 ,就会datePicker.valueProperty()恢复到其原始值。

这似乎不是“按设计工作”,并且多个其他开发人员已确认该问题在更高版本的 Java 中不存在。

这是一个 MCVE 来演示:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.DatePicker;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class DatePickerCommit extends Application {

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

    @Override
    public void start(Stage primaryStage) {

        // Simple Interface
        VBox root = new VBox(10);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(10));

        DatePicker datePicker = new DatePicker();

        // Add listener on DatePicker
        datePicker.valueProperty().addListener((observable, oldValue, newValue) -> {

            if (newValue != null) {

                // Show an Alert
                Alert alert = new Alert(Alert.AlertType.WARNING);
                alert.setContentText("You selected " + newValue);
                alert.show();
                alert.setY(alert.getY()+100);
            }

        });

        root.getChildren().add(datePicker);

        // Show the stage
        primaryStage.setScene(new Scene(root));
        primaryStage.setTitle("Sample");
        primaryStage.show();
    }
}
Run Code Online (Sandbox Code Playgroud)

Alert本身显示正确的日期,但DatePicker valueProperty()恢复到null,在这个截图所示:

截屏

使用 IntelliJ 的调试器,我可以在(或) 调用后datePicker.valueProperty()立即确认设置为“空” 。alert.show()alert.showAndWait()

最近的潜在已知错误:

我能够找到一些似乎相关的已知错误,但它们都被标记为自 8u73 起已解决(可能是回归?):

Sai*_*dem 2

我能够在我当前正在使用的 JDK 版本中重现此问题。因此,经过调查,根本原因是在将值提交给 textField 之前将焦点转移到新阶段(或从 DatePicker 失去焦点)。

在调试时我注意到以下结果:

  1. 当窗口焦点丢失时,焦点将转向所有子节点。
  2. 当焦点丢失时, ComboBoxPopupControl 调用setTextFromTextFieldIntoComboBoxValue方法。此时,如果您查看方法中的值,则 textField 的文本为空,并且“”为 null,导致将 null 值设置为comboBoxBase(突出显示的行)。

在此输入图像描述 我认为我们可以通过两种方式进行调整:

选项#1: 以另一种方式采用@Kleopatra 解决方案,即在显示警报之前设置文本。这样我们就欺骗了ComboBoxPopupControl -> setTextFromTextFieldIntoComboBoxValue 方法,让其认为 textField 中有一个有效值,而不让它重置该值。

选项#2: 将显示警报的部分包装在Platform.runLater中,以处理稍后执行时显示的警报(此时提交将已在 DatePicker 中执行)。

这适用于手动输入的日期和在弹出窗口中选择的日期。

不确定此解决方法是否适合您。你可以尝试一下吗?

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.DatePicker;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.time.LocalDate;

public class DatePickerCommit extends Application {

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

    @Override
    public void start(Stage primaryStage) {

        // Simple Interface
        VBox root = new VBox(10);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(10));

        DatePicker datePicker = new DatePicker();

        // Add listener on DatePicker
        datePicker.valueProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue != null) {
                // Option#1
               datePicker.getEditor().setText(datePicker.getConverter().toString(newValue));
               showAlert(newValue);

               // Option#2
               //Platform.runLater(()->showAlert(newValue));
            }
        });

        root.getChildren().add(datePicker);

        // Show the stage
        primaryStage.setScene(new Scene(root));
        primaryStage.setTitle("Sample");
        primaryStage.show();
    }

    private void showAlert(LocalDate value){
        Alert alert = new Alert(Alert.AlertType.WARNING);
        alert.setContentText("You selected " + value);
        alert.show();
        alert.setY(alert.getY()+100);
    }
}
Run Code Online (Sandbox Code Playgroud)