Выберите несколько дат с помощью DatePicker - PullRequest
4 голосов
/ 07 марта 2020

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

enter image description here

Я не хочу использовать частный API. Я думал о добавлении этого:

datePicker.setOnHiding(event -> {            
    event.consume();
});

Но это не работает.

Вот мой код:

public static DatePicker getDatePicker() {
    ObservableList<LocalDate> selectedDates = FXCollections.observableArrayList();
    String pattern = "yyyy-MM-dd";
    DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(pattern);
    DatePicker datePicker = new DatePicker();
    datePicker.setPromptText(pattern);

    datePicker.setConverter(new StringConverter<LocalDate>() {
        @Override
        public String toString(LocalDate date) {
            return (date == null) ? "" : dateFormatter.format(date);
        }

        @Override
        public LocalDate fromString(String string) {
            return ((string == null) || string.isEmpty()) ? null : LocalDate.parse(string, dateFormatter);
        }
    });

    datePicker.setOnAction(event -> {
        selectedDates.add(datePicker.getValue());
        event.consume();
    });

    datePicker.setDayCellFactory((DatePicker param) -> new DateCell() {
        @Override
        public void updateItem(LocalDate item, boolean empty) {
            super.updateItem(item, empty);
            boolean alreadySelected = selectedDates.contains(item);
            setDisable(alreadySelected);
            setStyle(alreadySelected ? "-fx-background-color: #09a30f;" : "");
        }
    });

    return datePicker;
}

Ответы [ 2 ]

2 голосов
/ 07 марта 2020

Если вы отметите класс DatePickerContent, вы обнаружите, что каждый раз, когда создается новый DateCell, к нему добавляется EventHandler типа MOUSE_CLICKED. Этот обработчик будет вызывать selectDayCell(DateCell), когда пользователь нажимает на ячейку. selectDayCell(DateCell) устанавливает новое значение даты и скрывает DatePicker:

protected void createDayCells() {
    final EventHandler<MouseEvent> dayCellActionHandler = ev -> {
        if (ev.getButton() != MouseButton.PRIMARY) {
            return;
        }
        DateCell dayCell = (DateCell)ev.getSource();
        selectDayCell(dayCell);
        lastFocusedDayCell = dayCell;
    };
    for (int row = 0; row < 6; row++) {
        for (int col = 0; col < daysPerWeek; col++) {
            DateCell dayCell = createDayCell();
            dayCell.addEventHandler(MouseEvent.MOUSE_CLICKED, dayCellActionHandler);
            dayCells.add(dayCell);
        }
    }
    dayCellDates = new LocalDate[6 * daysPerWeek];
}

public void selectDayCell(DateCell dateCell) {
    datePicker.setValue(dayCellDate(dateCell));
    datePicker.hide();
}

Если вы используете Java 9 или новее, вы можете расширить класс DatePickerContent и переопределить метод selectDayCell(DateCell) чтобы не скрыть DatePicker после выбора ячейки:

public void selectDayCell(DateCell dateCell) {
    datePicker.setValue(dayCellDate(dateCell));
}

К сожалению, в Java 8, DatePickerContent имеет конструктор частного пакета, поэтому вы не можете расширять его. В качестве обходного пути вы можете добавить еще один EventHandler по щелчку мыши, который будет снова показывать DatePicker после нажатия на ячейку:

EventHandler<MouseEvent> mouseClickedEventHandler = clickEvent -> {
    if (clickEvent.getButton() == MouseButton.PRIMARY) {
        datePicker.show();
    }
    clickEvent.consume();
};

На фабрике вашей ячейки:

@Override
public void updateItem(LocalDate item, boolean empty) {
    super.updateItem(item, empty);
    //...
    if (item != null && !empty) {
        //...
        addEventHandler(MouseEvent.MOUSE_CLICKED, mouseClickedEventHandler);
    } else {
        //...
        removeEventHandler(MouseEvent.MOUSE_CLICKED, mouseClickedEventHandler);
    }
}
0 голосов
/ 10 марта 2020

Используя ответ MS, я смог создать это для JavaFX8:

enter image description here

public class MultiDatePicker
{

    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private final ObservableSet<LocalDate> selectedDates;
    private final DatePicker datePicker;

    public MultiDatePicker()
    {
        this.selectedDates = FXCollections.observableSet(new TreeSet<>());
        this.datePicker = new DatePicker();
        setUpDatePicker();
    }


    public ObservableSet<LocalDate> getSelectedDates()
    {
        return this.selectedDates;
    }

    public DatePicker getDatePicker()
    {
        return this.datePicker;
    }

    private void setUpDatePicker()
    {
        this.datePicker.setConverter(new StringConverter<LocalDate>()
        {
            @Override
            public String toString(LocalDate date)
            {
                return (date == null) ? "" : DATE_FORMAT.format(date);
            }

            @Override
            public LocalDate fromString(String string)
            {
                return ((string == null) || string.isEmpty()) ? null : LocalDate.parse(string, DATE_FORMAT);
            }
        });

        EventHandler<MouseEvent> mouseClickedEventHandler = (MouseEvent clickEvent) ->
        {
            if (clickEvent.getButton() == MouseButton.PRIMARY)
            {
                if (!this.selectedDates.contains(this.datePicker.getValue()))
                {
                    this.selectedDates.add(datePicker.getValue());

                } else
                {
                    this.selectedDates.remove(this.datePicker.getValue());

                    this.datePicker.setValue(getClosestDateInTree(new TreeSet<>(this.selectedDates), this.datePicker.getValue()));

                }

            }
            this.datePicker.show();
            clickEvent.consume();
        };

        this.datePicker.setDayCellFactory((DatePicker param) -> new DateCell()
        {
            @Override
            public void updateItem(LocalDate item, boolean empty)
            {
                super.updateItem(item, empty);

                //...
                if (item != null && !empty)
                {
                    //...
                    addEventHandler(MouseEvent.MOUSE_CLICKED, mouseClickedEventHandler);
                } else
                {
                    //...
                    removeEventHandler(MouseEvent.MOUSE_CLICKED, mouseClickedEventHandler);
                }

                if (selectedDates.contains(item))
                {

                    setStyle("-fx-background-color: rgba(3, 169, 244, 0.7);");

                } else
                {
                    setStyle(null);

                }
            }
        });

    }

    private static LocalDate getClosestDateInTree(TreeSet<LocalDate> dates, LocalDate date)
    {
        Long lower = null;
        Long higher = null;

        if (dates.isEmpty())
        {
            return null;
        }

        if (dates.size() == 1)
        {
            return dates.first();
        }

        if (dates.lower(date) != null)
        {
            lower = Math.abs(DAYS.between(date, dates.lower(date)));
        }
        if (dates.higher(date) != null)
        {
            higher = Math.abs(DAYS.between(date, dates.higher(date)));
        }

        if (lower == null)
        {
            return dates.higher(date);
        } else if (higher == null)
        {
            return dates.lower(date);
        } else if (lower <= higher)
        {
            return dates.lower(date);
        } else if (lower > higher)
        {
            return dates.higher(date);
        } else
        {
            return null;
        }
    }

}
...