在 Javafx datepickerskin 上突出显示多天

问题描述 投票:0回答:1

我正在尝试创建一个小应用程序,让用户执行以下操作:

  • 在文本字段中以 dd/mm/yyyy 格式输入日期
  • 按下按钮
  • 在一个日期选择器皮肤上显示所选日期并突出显示该日期以及之前的 5 天。

我知道我必须使用单元工厂,并且我在 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用户打开应用程序,或者我添加多个我不想要的应用程序。

感谢您的帮助。

java javafx datepicker
1个回答
0
投票

我不确定弄乱皮肤有什么意义。没有必要这样做。

如果我正确理解您的任务,您只想以不同的颜色显示所选日期之前的日子。您可以在 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);
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.