java - 如何使 ComboBox CellFactory 正确地动态禁用项目?

标签 java javafx combobox

我有几个用于为约会调度程序应用程序选择日期和时间的ComboBox,但是当我自定义CellFactory时,它只能正确禁用正确的项目第一次单击并展开时。基本上,它应该禁用允许您选择 LocalDateTime.now() 之前的日期和时间的选择。以下视频显示了异常行为: https://www.youtube.com/watch?v=niGoIvVx9Y0

这是我的细胞工厂的代码:

startHour.setCellFactory((final ListView<String> param) -> {
        return new ComboBoxListCell<String>() {
            @Override
            public void updateItem(String item, boolean empty) {
                super.updateItem(item, empty);
                if(empty || item == null) {
                    setText(null);
                    setGraphic(null);
                } else {
                    LocalDateTime selected = createDateTime(startDate.getValue(), item,
                            startMinute.getValue(), startMeridian.getValue());
                    if(selected.isBefore(LocalDateTime.now())) {
                        System.out.print(item + ", ");
                        setDisable(true);
                        setStyle(COMBOCELL_DISABLED_COLOR);
                    }
                }
            }
        };
    });

    startMinute.setCellFactory((final ListView<String> listView) -> {
        return new ComboBoxListCell<String>() {
            @Override
            public void updateItem(String item, boolean empty) {
                super.updateItem(item, empty);
                if(empty || item == null) {
                    setText(null);
                    setGraphic(null);
                } else {
                    LocalDateTime selected = createDateTime(startDate.getValue(), startHour.getValue(),
                            item, startMeridian.getValue());
                    if(selected.isBefore(LocalDateTime.now())) {
                        setDisable(true);
                        setStyle(COMBOCELL_DISABLED_COLOR);
                    }
                }
            }
        };
    });

    startMeridian.setCellFactory((final ListView<String> listView) -> {
        return new ComboBoxListCell<String>() {
            @Override public void updateItem(String item, boolean empty) {
                super.updateItem(item, empty);
                if(empty || item == null) {
                    setText(null);
                    setGraphic(null);
                } else {
                    LocalDateTime selected = createDateTime(startDate.getValue(), startHour.getValue(),
                            startMinute.getValue(), item);
                    if(selected.isBefore(LocalDateTime.now())) {
                        setDisable(true);
                        setStyle(COMBOCELL_DISABLED_COLOR);
                    }
                }
            }
        };
    });

所选的LocalDateTime是使用以下方法计算的:

private LocalDateTime createDateTime(LocalDate date, String hour, String minute, String meridian) {
    String dateTimeStr = date + " " + hour + " "  + minute + " " + meridian;
    return LocalDateTime.parse(dateTimeStr, DateTimeFormatter.ofPattern("yyyy-MM-dd h m a"));
}

startHour#setCellFactory 中,我插入了一个 println 语句,显示所选项目在 LocalDateTime 之前发生时转换为 LocalDateTime。 now(),这是我得到的输出:

1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 12, 12, 1, 9, 12, 1, 9, 12, 2, 1, 7, 9,

当前时间为上午 9:48,所选时间为上午 10:00,因此似乎满足预期条件。然而,正如视频所示,第一次扩展 ComboBox 后,行为非常不稳定。我有类似的 DatePicker 节点单元工厂,它们工作得很好。 我是否误解了 ComboBox#setCellFactory 的用途?

最佳答案

对于startHour ,当任何单元格的当前项目、选定的开始日期、选定的开始分钟或选定的子午线发生更改时,您需要(可能)更新单元格的禁用状态。您可以通过绑定(bind)来完成此操作。

(请注意,您使用了错误的单元格类别。ComboBoxListCell 用于在使用组合框作为编辑器的列表中显示可编辑单元格。您只需要一个普通的 ListCell 即可。)

startHour.setCellFactory((final ListView<String> param) -> {
    ListCell<String> cell = new ListCell<String>() {
        @Override
        public void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);
            setText(item); // works in empty case as item will be null, as desired
        }
    };

    cell.disableProperty().bind(Bindings.createBooleanBinding(
        () -> {
            if (cell.getItem() == null) return false ;
            LocalDateTime selected = createDateTime(startDate.getValue(), cell.getItem(),
                        startMinute.getValue(), startMeridian.getValue());
            return selected.isBefore(LocalDateTime.now());
        }, 
        cell.itemProperty(), 
        startMinute.valueProperty(), 
        startMeridian.valueProperty(), 
        startDate.valueProperty()
    ));

    return cell ;
});

其他细胞工厂将类似。

对于样式,我强烈建议您使用外部样式表,其规则如下:

.combo-box .list-cell:disabled {
    /* style for disabled cell here */
}

这使代码更加简单,并且意味着只有一个位置可以为所有相关的禁用组合框单元格定义样式。

请注意,一旦显示了代表当前时间之后的时间的单元格(因此它已启用),如果它显示的时间足够晚以至于不再晚于当前时间,则它不会自动禁用。 (例如,如果代表 2017 年 4 月 24 日下午 1 点的单元格在当天中午 12:55 首次显示,则该单元格将被启用。如果 10 分钟后再次显示该单元格且没有其他 UI 更改导致 updateItem()调用,它应该被禁用,但不会。)一个完整的解决方案将使绑定(bind)还观察“当前时间”的变化,为此您可以这样做:

ObjectProperty<LocalDateTime> clock = new SimpleObjectProperty<>(LocalDateTime.now());
Timeline clockwork = new Timeline(new KeyFrame(Duration.seconds(1), 
        e -> clock.set(LocalDateTime.now())));
clockwork.setCycleCount(Animation.INDEFINITE);
clockwork.play();

然后添加clock到绑定(bind)中的依赖值列表:

cell.disableProperty().bind(Bindings.createBooleanBinding(
    () -> {
        if (cell.getItem() == null) return false ;
        LocalDateTime selected = createDateTime(startDate.getValue(), cell.getItem(),
                    startMinute.getValue(), startMeridian.getValue());
        return selected.isBefore(clock.get());
    }, 
    cell.itemProperty(), 
    startMinute.valueProperty(), 
    startMeridian.valueProperty(), 
    startDate.valueProperty(),
    clock
));

关于java - 如何使 ComboBox CellFactory 正确地动态禁用项目?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43590075/

相关文章:

JavaFX 应用程序线程到任务通信

jquery - 使用 css 和 jquery 样式(突出显示/粗体)组合/选择框中的一组元素

java - Scanner.nextLine() 上的 NoSuchElementException

java - 当程序在 Eclipse IDE 中运行且没有任何错误时,无法在 java 小程序中使用 FileReader 打开文件

java - JavaFX:如何将TitledPane中的“下拉箭头”移动到右侧

java - 如何在 GridPane 上使用 onMouseClicked 修复 'Illegal Argument Exception'?

java - 如何使用 html 运行 Java 代码?

java - 升级到 gradle 4.4 和 gradle 构建工具版本 3.1.2 后,文件中出现红线

javascript - 在 HTML 中为组合框设置样式选择和输入元素

php - 如何在不刷新PHP页面的情况下更新MYSQL字段?