我正在尝试创建一个小应用程序,让用户执行以下操作:
我知道我必须使用单元工厂,并且我在 stackoverflow 和其他网站上找到了几个实现,但是在我的情况下没有一个实现,因此我的问题。我已经看到了不同的方法来实现我想要的,有或没有数组,我决定发布的方法似乎更优雅。
我不明白我的代码是否不起作用,或者出于某种原因代码无法覆盖默认主题。
我还遇到另一个问题:如果我不设置daycellfactory,用户可以使用应用程序一次输入多个日期并且按钮起作用,如果我设置daycellfactory,按钮只能起作用一次,我该怎么办?
这是我按钮的逻辑:
calculate_button.setOnAction(actionEvent -> {
//getting user input as string
String user_date = textField_start_of_period.getText();
//checking that the user input is in the format dd/mm/yyyy:
String regex_check = "[0-9]{2}\\/[0-9]{2}\\/[0-9]{4}";
if (user_date.matches(regex_check)) {
//transforming string into time
DateTimeFormatter datetimeformatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate localDate = LocalDate.parse(user_date, datetimeformatter);
//setting the entered date
today.setValue(localDate.plusDays(13));
//array with days I want to be highlited (today -days)
List<LocalDate> fertile_days = new ArrayList<>();
highlight_days.add(today.getValue().minusDays(5));
highlight_days.add(today.getValue().minusDays(4));
highlight_days.add(today.getValue().minusDays(3));
//highlighting days
final Callback<DatePicker, DateCell> dayCellFactory = new Callback<>() {
@Override
public DateCell call(final DatePicker datePicker) {
return new DateCell() {
@Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
if (highlight_days.contains(item)) {
this.setStyle("-fx-background-color: pink");
}
}
}
};
}
};
today.setDayCellFactory(dayCellFactory);
} else {
String errormessage = new String("error");
textField_start_of_period.setText(errormessage);
}
});
我尝试更改数组中的不同格式:
highlight_days.add(LocalDate.now().minusDays(5));
highlight_days.add(LocalDate.of(2024,Month.JANUARY,5));
我尝试直接设置daycellfactory,但它也不起作用:
today.setDayCellFactory(new Callback<DatePicker, DateCell>() {
@Override
public DateCell call(DatePicker param) {
return new DateCell(){
@Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
if(highlight.contains(item)) {
this.setStyle("-fx-background-color: pink");
//I thought maybe the default theme was overriding my code so I tried:
//this.getStyleClass().add("hightlight_days"); *
//but it doesn’t work
}
}
}
};
}
});
*这是stylesheet.css中的代码(放置在src/main/resources中):
.date-picker-popup > * > .date-cell.highlight_days {
-fx-background-color: pink;
}
今天的日期选择器与用户打开应用程序时显示的日期选择器相同,并显示当前日期 (LocalDate.now),并且一旦用户单击按钮,它就会被重复使用:
DatePicker today = new DatePicker(LocalDate.now());
DatePickerSkin datePickerSkin = new DatePickerSkin(today);
Node popupContent = datePickerSkin.getPopupContent();
这样做是因为 DatePickerSkin 默认情况下仅显示一天,因此当用户启动应用程序时必须选择一天(我不能将 DatePickerSkin 构造函数留空)。使用不同的 DatePickers 不起作用,因为 DatePickerSkin 只接受一个 DatePicker,这意味着添加更多 DatePickers 意味着添加更多 DatePickersSkin,然后必须使用必须添加到网格中的多个节点来显示它们,所以要么我在以下情况下不显示 DatePickerSkin用户打开应用程序,或者我添加多个我不想要的应用程序。
感谢您的帮助。
我不确定弄乱皮肤有什么意义。没有必要这样做。
如果我正确理解您的任务,您只想以不同的颜色显示所选日期之前的日子。您可以在 DateCell 中执行此操作,但需要一个额外的步骤:您还必须告诉 DatePicker 在所选日期更改时重新渲染每个单元格:
DatePicker picker = new DatePicker();
picker.setDayCellFactory(p -> new PrecedingDaysDateCell(p));
picker.valueProperty().addListener(o -> Platform.runLater(() -> {
picker.setDayCellFactory(null);
picker.setDayCellFactory(p -> new PrecedingDaysDateCell(p));
}));
上面的代码通过将单元格工厂设置为 null(默认值)来响应 DatePicker 的
value
属性中的任何更改,然后返回到将日期渲染为粉红色的单元格工厂,从而导致每个日期单元格都重新渲染。虽然很笨拙,但很有效。
(Platform.runLater 调用是必要的,因为在 DatePicker 仍在处理值更改时更改单元格工厂可能会导致内部不一致,从而导致异常。)
这似乎在所有情况下都对我有用:
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
import javafx.application.Platform;
import javafx.scene.control.DatePicker;
import javafx.scene.control.DateCell;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.paint.Color;
// For App class only
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
public class PrecedingDaysDateCell
extends DateCell {
private final DatePicker picker;
private final int precedingDays;
private final Background precedingDaysBackground = new Background(
new BackgroundFill(Color.PINK, null, null));
public PrecedingDaysDateCell(DatePicker picker) {
this(picker, 5);
}
public PrecedingDaysDateCell(DatePicker picker,
int precedingDays) {
this.picker = Objects.requireNonNull(picker,
"DatePicker cannot be null.");
this.precedingDays = precedingDays;
}
private void updateBackground(LocalDate date) {
LocalDate selectedDate = picker.getValue();
if (date != null && selectedDate != null) {
long days = ChronoUnit.DAYS.between(date, selectedDate);
if (days > 0 && days <= precedingDays) {
setBackground(precedingDaysBackground);
}
}
}
@Override
public void updateItem(LocalDate date,
boolean empty) {
super.updateItem(date, empty);
if (!empty) {
updateBackground(date);
}
}
@Override
public void updateSelected(boolean selected) {
if (!selected) {
updateBackground(getItem());
}
}
public static class App
extends Application {
@Override
public void start(Stage stage) {
DatePicker picker = new DatePicker();
picker.setDayCellFactory(p -> new PrecedingDaysDateCell(p));
picker.valueProperty().addListener(o -> Platform.runLater(() -> {
picker.setDayCellFactory(null);
picker.setDayCellFactory(p -> new PrecedingDaysDateCell(p));
}));
BorderPane pane = new BorderPane(picker);
pane.setPadding(new Insets(60));
stage.setScene(new Scene(pane));
stage.setTitle("Preceding Days DatePicker");
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
}