Trên ứng dụng tôi hiện đang làm việc, cần phải chọn một ngày hoặc một khoảng thời gian từ cùng một JavaFX 8 DatePicker.Chọn một khoảng thời gian hoặc ngày bằng cách sử dụng ONE JavaFX 8 DatePicker
Cách ưa thích để làm điều này sẽ như sau:
Lựa chọn một ngày duy nhất - tương tự như hành vi mặc định của DatePicker.
Chọn khoảng thời gian - chọn ngày bắt đầu/kết thúc bằng cách giữ nút chuột và kéo đến ngày kết thúc/ngày bắt đầu bạn muốn. Khi nút chuột được nhả, bạn đã xác định thời gian của bạn. Thực tế là bạn không thể chọn ngày khác với những ngày được hiển thị có thể chấp nhận được.
Editing nên làm việc cho cả ngày đơn (ví dụ 2014/12/24) và thời gian (ví dụ: 2014/12/24 - 27.12.2014)
Một vẽ có thể có của giai đoạn lựa chọn (trừ nội dung của soạn thảo văn bản) ở trên sẽ giống như thế này:
đâu màu cam cho ngày hiện tại, màu xanh chỉ ra khoảng thời gian được chọn. Hình ảnh là từ một nguyên mẫu tôi đã tạo, nhưng khi khoảng thời gian được chọn bằng cách sử dụng 2 DatePickers thay vì một.
tôi đã có một cái nhìn tại các sourcecode cho
com.sun.javafx.scene.control.skin.DatePickerContent
trong đó có một
protected List<DateCell> dayCells = new ArrayList<DateCell>();
để tìm ra một cách để phát hiện khi chuột chọn một ngày kết thúc khi con chuột được phát hành (hoặc có thể phát hiện một kéo).
Tuy nhiên tôi không chắc chắn về cách thực hiện. Bất kỳ đề xuất?
Tôi đang gắn mã mẫu đơn giản mà tôi đã thực hiện cho đến thời điểm này (sử dụng 2 chứ không phải là 1 datepicker mong muốn).
import java.time.LocalDate;
import javafx.beans.property.SimpleObjectProperty;
public interface PeriodController {
/**
* @return Today.
*/
LocalDate currentDate();
/**
* @return Selected from date.
*/
SimpleObjectProperty<LocalDate> fromDateProperty();
/**
* @return Selected to date.
*/
SimpleObjectProperty<LocalDate> toDateProperty();
}
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import javafx.util.StringConverter;
public class DateConverter extends StringConverter<LocalDate> {
private DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy"); // TODO i18n
@Override
public String toString(LocalDate date) {
if (date != null) {
return dateFormatter.format(date);
} else {
return "";
}
}
@Override
public LocalDate fromString(String string) {
if (string != null && !string.isEmpty()) {
return LocalDate.parse(string, dateFormatter);
} else {
return null;
}
}
}
import static java.lang.System.out;
import java.time.LocalDate;
import java.util.Locale;
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.geometry.HPos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class PeriodMain extends Application {
private Stage stage;
public static void main(String[] args) {
Locale.setDefault(new Locale("no", "NO"));
launch(args);
}
@Override
public void start(Stage stage) {
this.stage = stage;
stage.setTitle("Period prototype ");
initUI();
stage.getScene().getStylesheets().add(getClass().getResource("/period-picker.css").toExternalForm());
stage.show();
}
private void initUI() {
VBox vbox = new VBox(20);
vbox.setStyle("-fx-padding: 10;");
Scene scene = new Scene(vbox, 400, 200);
stage.setScene(scene);
final PeriodPickerPrototype periodPickerPrototype = new PeriodPickerPrototype(new PeriodController() {
SimpleObjectProperty<LocalDate> fromDate = new SimpleObjectProperty<>();
SimpleObjectProperty<LocalDate> toDate = new SimpleObjectProperty<>();
{
final ChangeListener<LocalDate> dateListener = (observable, oldValue, newValue) -> {
if (fromDate.getValue() != null && toDate.getValue() != null) {
out.println("Selected period " + fromDate.getValue() + " - " + toDate.getValue());
}
};
fromDate.addListener(dateListener);
toDate.addListener(dateListener);
}
@Override public LocalDate currentDate() {
return LocalDate.now();
}
@Override public SimpleObjectProperty<LocalDate> fromDateProperty() {
return fromDate;
}
@Override public SimpleObjectProperty<LocalDate> toDateProperty() {
return toDate;
}
});
GridPane gridPane = new GridPane();
gridPane.setHgap(10);
gridPane.setVgap(10);
Label checkInlabel = new Label("Check-In Date:");
GridPane.setHalignment(checkInlabel, HPos.LEFT);
gridPane.add(periodPickerPrototype, 0, 1);
vbox.getChildren().add(gridPane);
}
}
import java.time.LocalDate;
import javafx.beans.value.ChangeListener;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.DateCell;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
import javafx.util.Callback;
import javafx.util.StringConverter;
/**
* Selecting a single date or a period - only a prototype.
* As long as you have made an active choice on the {@code toDate}, the {@code fromDate} and {@code toDate} will have the same date.
*/
public class PeriodPickerPrototype extends GridPane {
private static final String CSS_CALENDAR_BEFORE = "calendar-before";
private static final String CSS_CALENDAR_BETWEEN = "calendar-between";
private static final String CSS_CALENDAR_TODAY = "calendar-today";
private static final boolean DISPLAY_WEEK_NUMBER = true;
private Label fromLabel;
private Label toLabel;
private DatePicker fromDate;
private DatePicker toDate;
private StringConverter<LocalDate> converter;
private PeriodController controller;
private ChangeListener<LocalDate> fromDateListener;
private ChangeListener<LocalDate> toDateListener;
private Callback<DatePicker, DateCell> toDateCellFactory;
private Callback<DatePicker, DateCell> fromDateCellFactory;
private Tooltip todayTooltip;
private boolean toDateIsActivlyChosenbyUser;
public PeriodPickerPrototype(final PeriodController periodController)
{
this.controller = periodController;
createComponents();
makeLayout();
createHandlers();
bindAndRegisterHandlers();
i18n();
initComponent();
}
public void createComponents() {
fromLabel = new Label();
toLabel = new Label();
fromDate = new DatePicker();
toDate = new DatePicker();
todayTooltip = new Tooltip();
}
public void createHandlers() {
fromDate.setOnAction(event -> {
if ((!toDateIsActivlyChosenbyUser) || fromDate.getValue().isAfter(toDate.getValue())) {
setDateWithoutFiringEvent(fromDate.getValue(), toDate);
toDateIsActivlyChosenbyUser = false;
}
});
toDate.setOnAction(event -> toDateIsActivlyChosenbyUser = true);
fromDateCellFactory = new Callback<DatePicker, DateCell>() {
@Override public DateCell call(final DatePicker datePicker) {
return new DateCell() {
@Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
getStyleClass().removeAll(CSS_CALENDAR_TODAY, CSS_CALENDAR_BEFORE, CSS_CALENDAR_BETWEEN);
if ((item.isBefore(toDate.getValue()) || item.isEqual(toDate.getValue())) && item.isAfter(fromDate.getValue())) {
getStyleClass().add(CSS_CALENDAR_BETWEEN);
}
if (item.isEqual(controller.currentDate())) {
getStyleClass().add(CSS_CALENDAR_TODAY);
setTooltip(todayTooltip);
} else {
setTooltip(null);
}
}
};
}
};
toDateCellFactory =
new Callback<DatePicker, DateCell>() {
@Override
public DateCell call(final DatePicker datePicker) {
return new DateCell() {
@Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
setDisable(item.isBefore(fromDate.getValue()));
getStyleClass().removeAll(CSS_CALENDAR_TODAY, CSS_CALENDAR_BEFORE, CSS_CALENDAR_BETWEEN);
if (item.isBefore(fromDate.getValue())) {
getStyleClass().add(CSS_CALENDAR_BEFORE);
} else if (item.isBefore(toDate.getValue()) || item.isEqual(toDate.getValue())) {
getStyleClass().add(CSS_CALENDAR_BETWEEN);
}
if (item.isEqual(controller.currentDate())) {
getStyleClass().add(CSS_CALENDAR_TODAY);
setTooltip(todayTooltip);
} else {
setTooltip(null);
}
}
};
}
};
converter = new DateConverter();
fromDateListener = (observableValue, oldValue, newValue) -> {
if (newValue == null) {
// Restting old value and cancel..
setDateWithoutFiringEvent(oldValue, fromDate);
return;
}
controller.fromDateProperty().set(newValue);
};
toDateListener = (observableValue, oldValue, newValue) -> {
if (newValue == null) {
// Restting old value and cancel..
setDateWithoutFiringEvent(oldValue, toDate);
return;
}
controller.toDateProperty().set(newValue);
};
}
/**
* Changes the date on {@code datePicker} without fire {@code onAction} event.
*/
private void setDateWithoutFiringEvent(LocalDate newDate, DatePicker datePicker) {
final EventHandler<ActionEvent> onAction = datePicker.getOnAction();
datePicker.setOnAction(null);
datePicker.setValue(newDate);
datePicker.setOnAction(onAction);
}
public void bindAndRegisterHandlers() {
toDate.setDayCellFactory(toDateCellFactory);
fromDate.setDayCellFactory(fromDateCellFactory);
fromDate.valueProperty().addListener(fromDateListener);
fromDate.setConverter(converter);
toDate.valueProperty().addListener(toDateListener);
toDate.setConverter(converter);
}
public void makeLayout() {
setHgap(6);
add(fromLabel, 0, 0);
add(fromDate, 1, 0);
add(toLabel, 2, 0);
add(toDate, 3, 0);
fromDate.setPrefWidth(120);
toDate.setPrefWidth(120);
fromLabel.setId("calendar-label");
toLabel.setId("calendar-label");
}
public void i18n() {
// i18n code replaced with
fromDate.setPromptText("dd.mm.yyyy");
toDate.setPromptText("dd.mm.yyyy");
fromLabel.setText("From");
toLabel.setText("To");
todayTooltip.setText("Today");
}
public void initComponent() {
fromDate.setTooltip(null); // Ønsker ikke tooltip
setDateWithoutFiringEvent(controller.currentDate(), fromDate);
fromDate.setShowWeekNumbers(DISPLAY_WEEK_NUMBER);
toDate.setTooltip(null); // Ønsker ikke tooltip
setDateWithoutFiringEvent(controller.currentDate(), toDate);
toDate.setShowWeekNumbers(DISPLAY_WEEK_NUMBER);
}
}
/** period-picker.css goes udner resources (using maven) **/
.date-picker {
/* -fx-font-size: 11pt;*/
}
.calendar-before {
}
.calendar-between {
-fx-background-color: #bce9ff;
}
.calendar-between:hover {
-fx-background-color: rgb(0, 150, 201);
}
.calendar-between:focused {
-fx-background-color: rgb(0, 150, 201);
}
.calendar-today {
-fx-background-color: rgb(255, 218, 111);
}
.calendar-today:hover {
-fx-background-color: rgb(0, 150, 201);
}
.calendar-today:focused {
-fx-background-color: rgb(0, 150, 201);
}
#calendar-label {
-fx-font-style: italic;
-fx-fill: rgb(75, 75, 75);
-fx-font-size: 11;
}
này là phù hợp với những gì tôi đang tìm kiếm. Tôi sẽ đăng lại một giải pháp mạnh mẽ hơn sau này. Tôi cần để có thể chọn ngày vào tháng tới, vì vậy thay vì sử dụng 'getText' để lấy ngày, tôi sẽ sử dụng sự phản chiếu để truy cập 'dayCellDate' trên 'DatePickerContent' để xác định chính xác 'LocalDate'. Ngoài ra, khi lựa chọn được thực hiện, nó sẽ đóng, vì vậy tôi sẽ thêm một 'datePicker.hide();'. Nó sẽ làm việc cả hai phía sau và phía trước để thêm một phương pháp đầu tiên/cuối cùng sẽ cho phép bạn chọn 25 đầu tiên và 11 cuối cùng (trong ví dụ). Tôi đánh giá cao bằng chứng về khái niệm của bạn. – Skjalg
Cảm ơn. Bởi 'tháng tiếp theo' bạn có nghĩa là chọn ngày đã được hiển thị trong cùng một lưới, phải không? Tôi chỉ cần loại bỏ chúng khỏi việc lựa chọn ô để đơn giản. Chúng có kiểu 'next-month', do đó bạn sẽ không cần phản chiếu, chỉ cần đặt cho' datePicker.getValue(). GetMonth() + 1' –
Vâng, ý tôi là chọn các ngày allready được hiển thị. Trong hình trên bạn có thể chọn ví dụ 31. Tháng 12 đến 3 tháng 1. Tôi nhận ra 'tháng tiếp theo' đã có để đơn giản. Tôi nhận ra nhiều người chống lại việc sử dụng sự phản chiếu trong OO, nhưng trong opionion của tôi nó có ý nghĩa hơn ở đây để sử dụng một phương pháp hiện có hơn là để tính toán nó, tuy nhiên simpe. – Skjalg