Использовать текущее значение DatePicker в качестве минимального / максимального значения другого DatePicker - PullRequest
3 голосов
/ 30 мая 2019

У меня есть пользовательский интерфейс JavaFX, который позволяет пользователю указать промежуток времени.Он состоит из двух DatePicker, один для выбора даты начала и один для выбора даты окончания промежутка времени.

Очевидно, я не хочу, чтобы пользователь мог выбирать дату начала, которая следует после даты окончания, и наоборот.

Я уже настроил пользовательский 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, где вы можете связать минимальное значение концаDate DatePicker для текущего значения даты начала DatePicker, а максимальное значение начальной даты DatePicker для текущего значения даты окончания DatePicker.Как мне добиться такого поведения в JavaFX?

1 Ответ

5 голосов
/ 30 мая 2019

вы можете передать первое значение 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
            }
        }

    }

}

Iсоздали два разных класса, но я уверен, что это можно решить с помощью какой-то необычной функции java8, чтобы сделать ее гораздо более общей, чем вы могли бы использовать в этом случае, если есть какое-либо условие, чтобы отключить дату.

Update

Вот общее решение:

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));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...