使用DatePicker的当前值作为另一个DatePicker的最小值/最大值

Igg*_*lob 3 java javafx

我有一个JavaFX UI,允许用户指定时间跨度。它包含两个DatePicker,一个用于选择时间范围的开始日期,另一个用于选择时间范围的结束日期。

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

我已经DayCellFactory为两个日期选择器设置了自定义,如/sf/answers/3246106601/中所述。该操作按预期的方式在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));
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Sun*_*ame 6

您可以将第一个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
            }
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

我创建了两个不同的类,但是我敢肯定,可以使用一些精美的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.
            }
        }

    }
Run Code Online (Sandbox Code Playgroud)

可以像这样使用:

// 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));
Run Code Online (Sandbox Code Playgroud)