Gabriele
Gabriele

Reputation: 1

Remove Datepicker rendering from a TableView cell (JavaFX8)

I have a TableView with 2 columns “Date” (LocalDate) and “FX” (Double). I have enabled the cell editing and following an example I found here (http://physalix.com/javafx8-render-a-datepicker-cell-in-a-tableview/) I have created a custom CellFactory that displays a DatePicker for the cells of column “Date”. This solution though renders the DatePciker immediately, so I changed my code to show the DatePicker only when the user double clicks on any of the (non-empty) Date cells. So far so good…

How do I “go back” and remove the DatePicker rendering from the cell after the user has changed the date or cancelled the input? See the pictures as reference. Pic 1 is the initial state of the list. Pic 2 is after double click. How do I go back to Pic 1 status? Let me know if you need to see my specific code.

Reference pictures

This is the code that checks for the double click and then creates the CellFactory

 fxTable.getSelectionModel().setCellSelectionEnabled(true);
    fxTable.setOnMouseClicked(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            if (event.getClickCount() == 2) {
                TablePosition pos = fxTable.getSelectionModel().getSelectedCells().get(0);
                int col = pos.getColumn();
                if (col == 0) {

                    //The code below creates the DatePicker in the cell using the DatePickerCell class that I created following the example in the code I found
                    tblDateFX.setCellFactory(new Callback<TableColumn<Map.Entry<LocalDate, Double>, String>, TableCell<Map.Entry<LocalDate, Double>, String>>() {
                        @Override
                        public TableCell<Map.Entry<LocalDate, Double>, String> call(TableColumn<Map.Entry<LocalDate, Double>, String> param) {
                            ObservableMap<LocalDate, Double> items = FXCollections.observableMap(myBasket.getEnrtriesCur(curName));

                            DatePickerCell datePick = new DatePickerCell(items);
                            return datePick;
                        }
                    });
                }
            }
        }
    });

This is the DatePickerCell Class

public class DatePickerCell<S, T> extends TableCell<Map.Entry<LocalDate,Double>, String> {

private DatePicker datePicker;
private ObservableMap<LocalDate,Double> curEntries;


public DatePickerCell(ObservableMap<LocalDate,Double> curEntries) {

    super();

    this.curEntries = curEntries;

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

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

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

    super.updateItem(item, empty);

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

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

        if (isEditing()) {

            setContentDisplay(ContentDisplay.TEXT_ONLY);

        } else {

            datePicker.setValue(LocalDate.parse(item,df));


            setGraphic(this.datePicker);
            setText(item);
            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);

        }
    }
}

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

@Override
public void cancelEdit() {
    super.cancelEdit();
    setContentDisplay(ContentDisplay.TEXT_ONLY);
    setGraphic(null);
}


private void createDatePicker() {
    this.datePicker = new DatePicker();
    datePicker.setEditable(true);

    datePicker.setOnMouseClicked(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            setGraphic(datePicker);
            setText(df.format(datePicker.getValue()));
        }
    });

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

            commitEdit(df.format(date));

            if (null != getCurEntries()) {
                System.out.println("Modify value");

            }
        }
    });

    setAlignment(Pos.CENTER);
}

Upvotes: 0

Views: 145

Answers (2)

Gabriele
Gabriele

Reputation: 1

After some research I found out that the default rendering of a cell in a TableView is a label. So I tweaked the DatePickerCell class to render a label in the "updateItem" method and render the DatePicker only when the label is clicked (meaning that the user wants to edit the date in the cell).

In terms of "going back" I added a listener for "ESC keypressed" on the DatePicker so when that key is pressed (during the edit) a label is rendered and the edit is therefore cancelled. That works quite well!

I'm still trying to figure out how to do the same when the user tries to cancel the edit by clicking somewhere else on the screen.

--

So here's my stab at the DatePickerEdit class.

This is doing what I need. Renders the cells normally at first, only when the user clicks on the date cell the datepicker is rendered. If the user clicks away from the cell, the cell goes back to its initial rendering (same happens when "ESC" is pressed whilst editing or indeed a new date is picked).

Note that I am passing to the class the Observable list that contains the values shown in the TableView. In this way I can update the value in the list directly in the class. Not sure if this is a good practice or not, this was a "forced solution" though. Originally I used the "setOnEditCommit" method for the TableColumn but after some testing I noticed that this event is not always called after the cell is updated (i.e. the commitEdit() method is called for the cell). Not sure if this is a bug or there's something wrong in my code. For sure it does not always happen. On multiple runs, I would say that 1 out of 3 showed this bugged behaviour.

Here's the code, not sure if it's a "good" code or not. I would appreciate any advice in merit.

public class DatePickerCell<S, T> extends TableCell<FX, String> {

    private DatePicker datePicker;
    private Label lbl;
    private ObservableList<FX> currencies;


    public DatePickerCell(ObservableList<FX> list) {

        super();

        lbl=new Label();
        this.currencies=list;
        if (datePicker == null) {
            createDatePicker();
        }

    }

    @Override
    public void updateItem(String item, boolean empty) {
//        This section here manages the graphic rendering of each cell
//        As I don't want to generate the datepicker graphics immediately I just render a label
        super.updateItem(item, empty);

        if (empty || item == null) {
            setText(null);
            setGraphic(null);
        } else {
            createLabel(item);
        }
    }

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

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

    private void createDatePicker() {
        this.datePicker = new DatePicker();
        datePicker.setEditable(true);

//        when the user clicks on the label the DatePicker graphics is generated
        lbl.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {

                datePicker.setValue(LocalDate.parse(lbl.getText(),df));
                setGraphic(datePicker);
                setText(lbl.getText());
                setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                datePicker.requestFocus();
            }
        });


//       This listener manages the "lost focus" on the picker
        datePicker.focusedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
//                This combination of OldValue NewValue is generated whenever there is a click outside the DatePicker "graphic area"
//                i.e. the calendar (when open), the text filed, the calendar icon OR when a NEW date is selected in the calendar.
//                 This last case generates the "OnAction" event as well that is managed below.
                if (oldValue && !newValue) {
                    createLabel(df.format(datePicker.getValue()));
                }
            }
        });

//        This is generated when a NEW date is picked
//        it simply commits the new date and changes the graphics back to a label
        datePicker.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                LocalDate date = datePicker.getValue();
                int index=getIndex();

                if (date!=null) {
                    commitEdit(df.format(date));
                    getCurrencies().get(index).setDate(date);
                    createLabel(df.format(date));
                }
            }
        });

//        added this listener in case the user wants to cancel pressing "ESC"
//        when this happens the label graphics is rendered
        datePicker.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                LocalDate date = datePicker.getValue();
                if (event.getCode()== KeyCode.ESCAPE) {
                    createLabel(df.format(date));
                }
            }
        });

        setAlignment(Pos.CENTER_LEFT);
    }

    private void createLabel(String item) {
        lbl.setMinWidth(getWidth());
        setGraphic(lbl);
        lbl.setText(item);
    }

    public ObservableList<FX> getCurrencies() {
        return currencies;
    }
}

Upvotes: 0

DeadPool
DeadPool

Reputation: 42

Have you tried the function setOnEditCommit to do reverse of your code?

column.setOnEditCommit((TableColumn.CellEditEvent<MyObject, Date> t) -> {
    //modify the rendering of you cell to normal
});

Upvotes: 0

Related Questions