简体   繁体   English

JavaFX自定义TableCell侦听

[英]JavaFX Custom TableCell Listening

The TextFieldTableCell, for example, responds and calls commitEdit() when you hit the enter key. 例如,当您按下Enter键时,TextFieldTableCell会响应并调用commitEdit()。

If I use a custom node to display, then how can I listen for ways to confirm an edit? 如果我使用自定义节点显示,那么我该如何侦听确认编辑的方法? For example, I have a DateTimePicker which extends a VBox. 例如,我有一个DateTimePicker扩展了VBox。 I can't add a KeyListener to a VBox. 我无法将KeyListener添加到VBox。

What can I do to watch for confirmation of in-table edits? 我该怎么做才能查看表内编辑的确认信息? Here is what I have attempted: 这是我尝试过的:

class TableCellDateTimePicker extends TableCell<CHILD, LocalDateTime> {

    public TableCellDateTimePicker() {
        super();
    }



    @Override
    public void startEdit() {
        super.startEdit();
        DateTimePicker picker = new DateTimePicker();
        picker.getDatePickerField().setOnKeyPressed((KeyEvent event) -> {
            System.out.println("PRESS");
            if (event.getCode() == KeyCode.ENTER) {
                commitEdit(picker.getValue());
            }
        });
        picker.setValue(getItem());
        setGraphic(picker);
    }



    @Override
    public void commitEdit(LocalDateTime newValue) {
        super.commitEdit(newValue);
        setText(newValue.toString());
    }



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

}

DateTimePicker: 的DateTimePicker:

/**
 * A custom DateTimePicker that lets a user select a LocalDateTime.
 *
 * @author Toni-Tran
 */
public class DateTimePicker extends VBox {

    @FXML
    private DatePicker datePicker;
    @FXML
    private Button showTimePickerBtn;

    private NumberPicker hourField;
    private NumberPicker minuteField;

    private ObjectProperty<LocalDateTime> currentLocalDateTimeValue;



    public DateTimePicker() {
        FXMLLoader loader = new FXMLLoader(this.getClass().getResource(
                ScreenPaths.DATE_TIME_PICKER));
        loader.setController(this);
        loader.setRoot(this);
        try {
            loader.load();
        } catch (IOException e) {
        }
    }



    @FXML
    private void initialize() {
        showTimePickerBtn.setOnAction(event -> this.showTimePicker());

        hourField = new NumberPicker();
        hourField.setBounds(0, 23);
        hourField.setWrapAround(true);

        minuteField = new NumberPicker();
        minuteField.setBounds(0, 59);
        minuteField.setWrapAround(true);

        currentLocalDateTimeValue = new SimpleObjectProperty<>();

        datePicker.valueProperty().addListener((ObservableValue<? extends LocalDate> observable,
                LocalDate oldValue, LocalDate newValue) -> {
                    LocalDateTime newLDT;
                    if (newValue != null) {
                        newLDT = LocalDateTime.of(newValue.getYear(),
                                newValue.getMonth(), newValue.getDayOfMonth(),
                                hourField.getValue(), minuteField.getValue());
                        currentLocalDateTimeValue.set(newLDT);
                    } else {
                        currentLocalDateTimeValue.set(null);
                    }
                });

        hourField.valueProperty().addListener((ObservableValue<? extends Number> observable,
                Number oldValue, Number newValue) -> {
                    if (currentLocalDateTimeValue.getValue() != null) {
                        LocalDateTime newLDT = LocalDateTime.of(currentLocalDateTimeValue.getValue().getYear(),
                                currentLocalDateTimeValue.getValue().getMonth(), currentLocalDateTimeValue.getValue().getDayOfMonth(),
                                hourField.getValue(), minuteField.getValue());
                        currentLocalDateTimeValue.set(newLDT);
                    } else {
                        currentLocalDateTimeValue.set(null);
                    }
                });

        minuteField.valueProperty().addListener((ObservableValue<? extends Number> observable,
                Number oldValue, Number newValue) -> {
                    if (currentLocalDateTimeValue.getValue() != null) {
                        LocalDateTime newLDT = LocalDateTime.of(currentLocalDateTimeValue.getValue().getYear(),
                                currentLocalDateTimeValue.getValue().getMonth(), currentLocalDateTimeValue.getValue().getDayOfMonth(),
                                hourField.getValue(), minuteField.getValue());
                        currentLocalDateTimeValue.set(newLDT);
                    } else {
                        currentLocalDateTimeValue.set(null);
                    }
                });
    }



    /**
     * Manually set a LocalDateTime value for this control.
     *
     * @param val The {@link LocalDateTime} value for this control.
     */
    public void setValue(LocalDateTime val) {
        if (val == null) {
            return;
        }
        LocalDate day = LocalDate.of(val.getYear(), val.getMonthValue(),
                val.getDayOfMonth());
        datePicker.setValue(day);
        hourField.setValue(val.getHour());
        minuteField.setValue(val.getMinute());
    }



    /**
     * Returns the currently set LocalDateTime.
     *
     * @return the {@link LocalDateTime} value of this control.
     */
    public LocalDateTime getValue() {
        return currentLocalDateTimeValue.getValue();
    }



    /**
     * Returns the Observable property of the DateTimePicker's current
     * LocalDateTime value.
     *
     * @return the Observable property of the DateTimePicker's current
     * LocalDateTime value.
     */
    public ObjectProperty<LocalDateTime> valueProperty() {
        return currentLocalDateTimeValue;
    }



    /**
     * This is triggered when the user wants to view the screen for setting a
     * certain time for their LocalDateTime.
     */
    public void showTimePicker() {
        HBox topContainer = new HBox();
        topContainer.getChildren().addAll(hourField, new Label(":"),
                minuteField);
        topContainer.setSpacing(3);
        DialogPopup.showNodeApplicationlModal(topContainer);
    }

}

Typically, an editable cell will display the default text when in non-editing mode and an editing control (your DateTimePicker ) when in editing mode. 通常,在非编辑模式下,可编辑单元格将显示默认文本,而在编辑模式下,可编辑单元格将显示编辑控件(您的DateTimePicker )。 Your custom cell implementation needs to handle the transition between those as well as making sure the text and value for the editing control are properly updated if the cell's item is updated. 您的自定义单元格实现需要处理它们之间的过渡,并确保如果更新了单元格的项目,则要正确更新编辑控件的文本和值。

So you should override updateItem(...) to update the value of the DateTimePicker if in editing mode ( isEditing() ) or the text if not. 因此,如果处于编辑模式( isEditing() ),则应重写updateItem(...)以更新DateTimePicker的值;否则,应覆盖文本。 Override startEdit() to switch to show the DateTimePicker instead of the text, and cancelEdit() to switch back. 覆盖startEdit()以切换为显示DateTimePicker而不是文本,并覆盖cancelEdit()以切换回。

You might not need to override commitEdit() at all. 您可能根本不需要重写commitEdit() If your column is bound to a property, the default commitEdit() should update that property (or you can do this in an onEditCommit handler registered with the column). 如果您的列绑定到一个属性,则默认的commitEdit()应该更新该属性(或者您可以在onEditCommit列注册的onEditCommit处理程序中执行此操作)。

The tutorial has an example of a fully-implemented editable table cell: see listing 13.11 near the bottom. 教程有一个完全实现的可编辑表格单元格的示例:请参见底部附近的清单13.11。

However, you will need to invoke commitEdit(...) at the appropriate time. 但是,您将需要在适当的时间调用commitEdit(...) The semantics of this are a bit tricky: you probably want to commit the edit if the user presses Enter when any of the individual controls inside your DateTimePicker have focus, and perhaps when the focus moves away from the DateTimePicker entirely. 这样做的语义有些棘手:当DateTimePicker内部的任何单个控件都具有焦点时,或者当焦点完全移离DateTimePicker时,如果用户按Enter键,则可能要提交编辑。

I would recommend creating an onAction property in your DateTimePicker class. 我建议您在DateTimePicker类中创建onAction属性。 This is pretty easy to do, you just have to call setEventHander(...) when the property changes. 这很容易做到,您只需在属性更改时调用setEventHander(...) There's a nice example in Richard Bair's MoneyField (there's a lot of stuff in that link, just look at where he creates the onAction handler). 理查德·贝尔Richard Bair)的MoneyField中有一个很好的例子(该链接中有很多东西,只看他在哪里创建了onAction处理程序)。 You can then (internally to the DateTimePicker ) check for action events (or perhaps Enter key presses) on the individual controls and call fireEvent(new ActionEvent(...)) when that happens. 然后,您可以(在DateTimePicker内部)检查单个控件上的动作事件(或按Enter键),并在发生这种情况时调用fireEvent(new ActionEvent(...)) Then your table cell implementation can register an onAction with the DateTimePicker and call commitEdit(...) . 然后,您的表格单元实现可以向DateTimePicker注册onAction并调用commitEdit(...)

Committing the edit on focus loss shouldn't be much harder. 进行聚焦损失的编辑应该不会困难得多。 Maybe expose a ReadOnlyBooleanProperty hasFocus() in DateTimePicker : 也许在DateTimePicker公开ReadOnlyBooleanProperty hasFocus()

private final ReadOnlyBooleanWrapper hasFocus = new ReadOnlyBooleanWrapper(this, "hasFocus");
hasFocus.bind(
     hourField.focusedProperty()
    .or(minuteField.focusedProperty())
    .or(datePicker.focusedProperty())
    .or(showTimePickerButton.focusedProperty()));

// ...
public ReadOnlyBooleanProperty hasFocusProperty() {
    return hasFocus.getReadOnlyProperty();
}

Then your cell implementation can observe that property and commit the edit if it changes to false . 然后,您的单元实现可以观察该属性,并在该属性更改为false提交编辑。 You may have to experiment with that a bit to get it to work satisfactorily. 您可能需要尝试一下才能使其令人满意地工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM