JavaFX8 – Render a DatePicker Cell in a TableView

octobre 3rd, 2014

Here is an example how to render a DatePicker in a TableCell in JavaFx8.

DatePickerTableCell

DatePickerCell class

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.Calendar;
import java.util.Date;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TableCell;

public class DatePickerCell<S, T> extends TableCell<BirthdayEvent, Date> {

    private DatePicker datePicker;
    private ObservableList<BirthdayEvent> birthdayData;

    public DatePickerCell(ObservableList<BirthdayEvent> listBirthdays) {

        super();
       
        this.birthdayData = listBirthdays;

        if (datePicker == null) {
            createDatePicker();
        }
        setGraphic(datePicker);
        setContentDisplay(ContentDisplay.GRAPHIC_ONLY);

        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                datePicker.requestFocus();
            }
        });
    }

    @Override
    public void updateItem(Date item, boolean empty) {

        super.updateItem(item, empty);

        SimpleDateFormat smp = new SimpleDateFormat("dd/MM/yyyy");

        if (null == this.datePicker) {
            System.out.println("datePicker is NULL");
        }

        if (empty) {
            setText(null);
            setGraphic(null);
        } else {

            if (isEditing()) {
                setContentDisplay(ContentDisplay.TEXT_ONLY);

            } else {
                setDatepikerDate(smp.format(item));
                setText(smp.format(item));
                setGraphic(this.datePicker);
                setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
            }
        }
    }

    private void setDatepikerDate(String dateAsStr) {

        LocalDate ld = null;
        int jour, mois, annee;

        jour = mois = annee = 0;
        try {
            jour = Integer.parseInt(dateAsStr.substring(0, 2));
            mois = Integer.parseInt(dateAsStr.substring(3, 5));
            annee = Integer.parseInt(dateAsStr.substring(6, dateAsStr.length()));
        } catch (NumberFormatException e) {
            System.out.println("setDatepikerDate / unexpected error " + e);
        }

        ld = LocalDate.of(annee, mois, jour);
        datePicker.setValue(ld);
    }

    private void createDatePicker() {
        this.datePicker = new DatePicker();
        datePicker.setPromptText("jj/mm/aaaa");
        datePicker.setEditable(true);

        datePicker.setOnAction(new EventHandler() {
            public void handle(Event t) {
                LocalDate date = datePicker.getValue();
                int index = getIndex();

                SimpleDateFormat smp = new SimpleDateFormat("dd/MM/yyyy");
                Calendar cal = Calendar.getInstance();
                cal.set(Calendar.DAY_OF_MONTH, date.getDayOfMonth());
                cal.set(Calendar.MONTH, date.getMonthValue() - 1);
                cal.set(Calendar.YEAR, date.getYear());

                setText(smp.format(cal.getTime()));
                commitEdit(cal.getTime());

                if (null != getBirthdayData()) {
                    getBirthdayData().get(index).setDate(cal.getTime());
                }
            }
        });

        setAlignment(Pos.CENTER);
    }

    @Override
    public void startEdit() {
        super.startEdit();
    }

    @Override
    public void cancelEdit() {
        super.cancelEdit();
        setContentDisplay(ContentDisplay.TEXT_ONLY);
    }
   
    public ObservableList<BirthdayEvent> getBirthdayData() {
        return birthdayData;
    }

    public void setBirthdayData(ObservableList<BirthdayEvent> birthdayData) {
        this.birthdayData = birthdayData;
    }

    public DatePicker getDatePicker() {
        return datePicker;
    }

    public void setDatePicker(DatePicker datePicker) {
        this.datePicker = datePicker;
    }

}

BirthdayEvent class

import java.text.SimpleDateFormat;
import java.util.Date;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class BirthdayEvent {

    private final SimpleObjectProperty<Date> date;
    private final StringProperty name;

    public BirthdayEvent(String name, Date date) {
        this.name = new SimpleStringProperty(name);
        this.date = new SimpleObjectProperty(date);
    }

    public String getName() {
        return name.get();
    }

    public void setName(String name) {
        this.name.set(name);
    }

    public StringProperty nameProperty() {
        return name;
    }

    public Date getDate() {
        return (Date) date.get();
    }

    public String getDateAsString() {
        SimpleDateFormat smp = new SimpleDateFormat("dd MMMMM yyyy");
        String strDate = (null == date || null == date.get())
                ? "" : smp.format(date.get());
       
        return strDate;
    }

    public void setDate(Date date) {
        this.date.set(date);
    }
}

Main class

import java.util.Date;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class Main extends Application {

    private static final String STYLESHEET_PATH = "/styles/styles.css";

    @Override
    public void start(Stage stage) {
        Scene scene = new Scene(new Group());
        scene.getStylesheets().add(STYLESHEET_PATH);
        stage.setTitle("DatePicker TableCell Sample");
        stage.setWidth(315);
        stage.setHeight(450);

        final ObservableList<BirthdayEvent> dataList
                = FXCollections.observableArrayList(
                        new BirthdayEvent("Jacob", new Date()),
                        new BirthdayEvent("Isabella", new Date()),
                        new BirthdayEvent("Ethan", new Date()),
                        new BirthdayEvent("Emma", new Date()),
                        new BirthdayEvent("Michael", new Date()));

        TableView table = new TableView();
        table.setEditable(true);

        TableColumn nameColumn = new TableColumn("Name");
        nameColumn.setPrefWidth(90);
        nameColumn.setCellValueFactory(
                new PropertyValueFactory<BirthdayEvent, String>("name"));

        TableColumn dateColumn = new TableColumn("Date");
        dateColumn.setEditable(true);
        dateColumn.setPrefWidth(75);
        dateColumn.setMinWidth(200);

        dateColumn.setCellValueFactory(new PropertyValueFactory<BirthdayEvent, Date>("date"));
        dateColumn.setCellFactory(new Callback<TableColumn, TableCell>() {
            @Override
            public TableCell call(TableColumn p) {
                DatePickerCell datePick = new DatePickerCell(dataList);
                return datePick;
            }
        });

        table.getColumns().addAll(nameColumn, dateColumn);
        table.setItems(dataList);

        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().add(table);

        ((Group) scene.getRoot()).getChildren().addAll(vbox);

        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

The source code of this sample is available on GitHub.

  • Flo

    Thanks a lot for that example, it’s working pretty good for me.

    There is only one thing I am struggling with at the moment: I want to set the TableColumn that includes the DatePicker to non-editable for specific user groups that I have stored in a database. If I try the usual dateColumn.setEditable(false); nothing happens and the DatePicker is still accessible for everyone. With a usual TextField in a TableView (e.g. the name column), it is working pretty good. Is there an easy way to set the dateColumn to non-Editable? Would be grateful for an answer, even if I am pretty sure that this is a stupid question and I am just missing out something I should definitely know…

    Cheers!