Eng.Fouad Eng.Fouad - 2 months ago 22
Java Question

JavaFX 8 DatePicker features

I just start using the new JavaFX 8 control

DatePicker
. In DatePicker User Experience Documentation, it is stated that it has couple of cool features that I would like to have in my GUI application:


  1. I want to change the format from
    mm/dd/yyyy
    to
    dd/mm/yyyy
    .

  2. I would like to restrict the date that can be selected. The user can only select from today until the same day of next year.

  3. Display Hijri dates besides the original ones:



enter image description here

How to implement these features? The JavaDoc doesn't say much about them.

Answer

Here is the full implementation:

import java.net.URL;
import java.time.LocalDate;
import java.time.chrono.HijrahChronology;
import java.time.format.DateTimeFormatter;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.DateCell;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.util.Callback;
import javafx.util.StringConverter;

/**
 *
 * @author Fouad
 */
public class FXMLDocumentController implements Initializable
{
    @FXML
    private DatePicker dpDate;

    @Override
    public void initialize(URL url, ResourceBundle rb)
    {
        dpDate.setValue(LocalDate.now());
        dpDate.setChronology(HijrahChronology.INSTANCE);

        Callback<DatePicker, DateCell> dayCellFactory = dp -> new DateCell()
        {
            @Override
            public void updateItem(LocalDate item, boolean empty)
            {
                super.updateItem(item, empty);

                if(item.isBefore(LocalDate.now()) || item.isAfter(LocalDate.now().plusYears(1)))
                {
                    setStyle("-fx-background-color: #ffc0cb;");
                    Platform.runLater(() -> setDisable(true));

                    /* When Hijri Dates are shown, setDisable() doesn't work. Here is a workaround */
                    //addEventFilter(MouseEvent.MOUSE_CLICKED, e -> e.consume());
                }
            }
        };

        StringConverter converter = new StringConverter<LocalDate>()
        {
            final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");

            @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())
                {
                    LocalDate date = LocalDate.parse(string, dateFormatter);

                    if(date.isBefore(LocalDate.now()) || date.isAfter(LocalDate.now().plusYears(1)))
                    {
                        return dpDate.getValue();
                    }
                    else return date;
                }
                else return null;
            }
        };

        dpDate.setDayCellFactory(dayCellFactory);
        dpDate.setConverter(converter);
        dpDate.setPromptText("dd/MM/yyyy");
    }

}