首页 > 解决方案 > 使用 DatePicker 的当前值作为另一个 DatePicker 的最小值/最大值

问题描述

我有一个允许用户指定时间跨度的 JavaFX UI。它由两个 DatePickers 组成,一个用于选择开始日期,一个用于选择时间跨度的结束日期。

显然,我不希望用户能够选择结束日期之后的开始日期,反之亦然。

我已经DayCellFactory为两个日期选择器设置了自定义,如https://stackoverflow.com/a/46372951/5801146中所述。这适用于在 DatePickers 初始化期间指定的初始时间范围。但是,如果用户随后选择了不同的开始日期,则结束日期 DatePicker 仍会显示初始可选日期,并且不会将其可选日期调整为新的开始日期。这是我使用属性侦听器的方法,当然,这不起作用:

public final class DatePickerUtil {

    public static void constrainLowerUpperBounds(DatePicker lowerBoundDatePicker, DatePicker upperBoundDatePicker) {
        LocalDate lowerBound = lowerBoundDatePicker.getValue();
        LocalDate upperBound = upperBoundDatePicker.getValue();

        if (lowerBound.isAfter(upperBound)) {
            throw new IllegalStateException("Upper bound date picker has older date than lower bound date picker");
        }

        lowerBoundDatePicker.setEditable(true);
        upperBoundDatePicker.setEditable(true);

        lowerBoundDatePicker.setDayCellFactory(createDayCellFactory(null, upperBound));
        upperBoundDatePicker.setDayCellFactory(createDayCellFactory(lowerBound, null));

        lowerBoundDatePicker.valueProperty().addListener((observable, oldValue, newValue) -> {
            upperBoundDatePicker.setDayCellFactory(createDayCellFactory(newValue, null));
        });

        upperBoundDatePicker.valueProperty().addListener(((observable, oldValue, newValue) -> {
            lowerBoundDatePicker.setDayCellFactory(createDayCellFactory(null, newValue));
        }));
    }

    private static Callback<DatePicker, DateCell> createDayCellFactory(LocalDate lowerBound, LocalDate upperBound) {
        return picker -> new DateCell() {
            @Override
            public void updateItem(LocalDate date, boolean empty) {
                super.updateItem(date, empty);
                boolean isBeforeLowerBound = lowerBound != null && date.isBefore(lowerBound);
                boolean isAfterUpperBound = upperBound != null && date.isAfter(upperBound);
                Platform.runLater(() -> setDisable(empty || isBeforeLowerBound || isAfterUpperBound));
            }
        };
    }
}

我想要实现的是某种绑定,例如在 WPF/UWP 中,您可以将结束日期 DatePicker 的最小值绑定到开始日期 DatePicker 的当前值,并将开始日期 DatePicker 的最大值绑定到当前值结束日期 DatePicker。如何在 JavaFX 中实现这种行为?

标签: javajavafx

解决方案


您可以将第一个DatePicker的值传递给第二个,并在它更改后更新第二个。

这是一个简单的例子:

public class MinMaxDate {

    private void test() {
        DatePicker firstDP = new DatePicker();
        DatePicker secondDP = new DatePicker();

        // set the min date
        secondDP.setDayCellFactory(cf -> new MinDateCell(firstDP.valueProperty()));
        // set the max date
        secondDP.setDayCellFactory(cf -> new MaxDateCell(firstDP.valueProperty()));
    }

    private class MinDateCell extends DateCell {

        private ObjectProperty<LocalDate> date;

        private MinDateCell(ObjectProperty<LocalDate> date) {
            this.date = date;
        }

        @Override
        public void updateItem(LocalDate item, boolean empty) {
            super.updateItem(item, empty);
            if (item.isBefore(date.get())) {
                this.setDisable(true);
                setStyle("-fx-background-color: #7e7e7e;"); // I used a different coloring to see which are disabled.
            }
        }

    }

    private class MaxDateCell extends DateCell {

        private ObjectProperty<LocalDate> date;

        private MaxDateCell(ObjectProperty<LocalDate> date) {
            this.date = date;
        }

        @Override
        public void updateItem(LocalDate item, boolean empty) {
            super.updateItem(item, empty);
            if (item.isAfter(date.get())) {
                this.setDisable(true);
                setStyle("-fx-background-color: #7e7e7e;"); // Same here
            }
        }

    }

}

我创建了两个不同的类,但我确信可以使用一些花哨的 java8 函数来解决它,使其更通用,然后如果有任何条件禁用日期,您可以使用它。

更新

这是通用解决方案:

private class FilterDateCell extends DateCell {

        private ObjectProperty<LocalDate> date;
        private BiPredicate<LocalDate, LocalDate> filterPredicate;

        private FilterDateCell(
                ObjectProperty<LocalDate> date,
                BiPredicate<LocalDate, LocalDate> filterPredicate) {
            this.date = date;
            this.filterFunction = filterFunction;
        }

        @Override
        public void updateItem(LocalDate item, boolean empty) {
            super.updateItem(item, empty);
            if (filterPredicate.test(item, date.get())) {
                this.setDisable(true);
                setStyle("-fx-background-color: #7e7e7e;"); // I used a different coloring to see which are disabled.
            }
        }

    }

它可以像这样使用:

// set the min date
secondDP.setDayCellFactory(cf -> new FilterDateCell(firstDP.valueProperty(),LocalDate::isBefore));
// set the max date
secondDP.setDayCellFactory(cf -> new FilterDateCell(firstDP.valueProperty(),LocalDate::isAfter));

推荐阅读