也许您可以使用Event Filter 来防止在按下回车键时进入编辑模式(并选择下面的单元格):
tableView.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
if(keyEvent.getCode() == KeyCode.ENTER) {
tableView.getSelectionModel().selectBelowCell();
keyEvent.consume();
}
});
按下数字键时开始编辑模式:
tableView.setOnKeyPressed(event -> {
TablePosition<Person, ?> pos = tableView.getFocusModel().getFocusedCell() ;
if (pos != null && event.getCode().isDigitKey()) {
tableView.edit(pos.getRow(), pos.getTableColumn());
}
});
编辑:包含的示例
package org.example;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.Event;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;
import java.util.stream.IntStream;
public class TableViewTestApp extends Application {
@Override
public void start(Stage stage) {
stage.setScene(new Scene(createTableView()));
stage.show();
}
private TableView<Item> createTableView() {
TableView<Item> tableView = new TableView<>();
tableView.setEditable(true);
tableView.getSelectionModel().setCellSelectionEnabled(true);
TableColumn<Item, String> nameCol = new TableColumn<>("name");
tableView.getColumns().add(nameCol);
nameCol.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
nameCol.setCellFactory(c -> new CustomTableCell());
// Enter edit mode on digit pressed only
tableView.setOnKeyPressed(event -> {
TablePosition<Item, ?> pos = tableView.getFocusModel().getFocusedCell();
if (pos != null && event.getCode().isDigitKey()) {
tableView.edit(pos.getRow(), pos.getTableColumn());
}
});
tableView.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
if (keyEvent.getCode() == KeyCode.ENTER) {
tableView.getSelectionModel().selectBelowCell(); //***When last cell of column move to the first cell of next column maybe
keyEvent.consume();
}
});
IntStream.range(1, 5).forEach(i -> tableView.getItems().add(new Item("Item " + i)));
return tableView;
}
private class CustomTableCell extends TableCell<Item, String> {
private TextField textField;
private void createTextField() {
textField = new TextField(getItem());
// Commit value on focus lost:
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (wasFocused) {
commitEdit(textField.getText());
}
});
// This is needed to commit last cell of the column on enter released:
// (***Alternative: Move to first cell of next column)
textField.addEventFilter(KeyEvent.KEY_RELEASED, keyEvent -> {
if (keyEvent.getCode() == KeyCode.ENTER) {
commitEdit(getItem());
keyEvent.consume();
}
});
}
@Override
public void startEdit() {
super.startEdit();
setText(null);
createTextField();
setGraphic(textField);
textField.requestFocus();
textField.selectAll();
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText(getItem());
setGraphic(null);
}
// See: https://gist.github.com/james-d/be5bbd6255a4640a5357
@Override
public void commitEdit(String item) {
// This block is necessary to support commit on losing focus, because the baked-in mechanism
// sets our editing state to false before we can intercept the loss of focus.
// The default commitEdit(...) method simply bails if we are not editing...
if (!isEditing() && !item.equals(getItem())) {
TableView<Item> table = getTableView();
if (table != null) {
TableColumn<Item, String> column = getTableColumn();
TableColumn.CellEditEvent<Item, String> event = new TableColumn.CellEditEvent<>(table,
new TablePosition<>(table, getIndex(), column),
TableColumn.editCommitEvent(), item);
Event.fireEvent(column, event);
}
}
super.commitEdit(item);
setText(item);
setGraphic(null);
textField = null;
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(item);
}
}
}
private class Item {
private final StringProperty name;
public Item(String name) {
this.name = new SimpleStringProperty(name);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
}
public static void main(String[] args) {
launch();
}
}
编辑 2:为整数添加自定义表格单元格(带微调器)
package org.example;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.WritableValue;
import javafx.event.Event;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.converter.IntegerStringConverter;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.function.UnaryOperator;
import java.util.stream.IntStream;
public class TableViewTestApp extends Application {
@Override
public void start(Stage stage) {
TableView<Item> tableView = createTableView();
Button btn = new Button("print items to console");
btn.setOnAction(actionEvent -> tableView.getItems().forEach(System.out::println));
stage.setScene(new Scene(new VBox(tableView, btn)));
stage.show();
}
private TableView<Item> createTableView() {
TableView<Item> tableView = new TableView<>();
tableView.setEditable(true);
tableView.getSelectionModel().setCellSelectionEnabled(true);
TableColumn<Item, String> nameCol = new TableColumn<>("name");
tableView.getColumns().add(nameCol);
nameCol.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
nameCol.setCellFactory(c -> new StringTextFieldTableCell());
// Enter edit mode on digit pressed only
tableView.setOnKeyPressed(event -> {
TablePosition<Item, ?> pos = tableView.getFocusModel().getFocusedCell();
if (pos != null && event.getCode().isDigitKey()) {
tableView.edit(pos.getRow(), pos.getTableColumn());
}
});
tableView.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
if (keyEvent.getCode() == KeyCode.ENTER) {
tableView.getSelectionModel().selectBelowCell(); //***When last cell of column move to the first cell of next column maybe
keyEvent.consume();
}
});
IntStream.range(1, 5).forEach(i -> tableView.getItems().add(new Item("Item " + i)));
TableColumn<Item, Integer> quantityCol = new TableColumn<>("quantity");
quantityCol.setCellValueFactory(cellData -> cellData.getValue().quantityProperty().asObject());
quantityCol.setCellFactory(c -> new IntegerSpinnerTableCell<>());
quantityCol.setMinWidth(150);
tableView.getColumns().add(quantityCol);
return tableView;
}
private class StringTextFieldTableCell extends TableCell<Item, String> {
private TextField textField;
private void createTextField() {
textField = new TextField(getItem());
// Commit value on focus lost:
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (wasFocused) {
commitEdit(textField.getText());
}
});
// This is needed to commit last cell of the column on enter released:
// (***Alternative: Move to first cell of next column)
textField.addEventFilter(KeyEvent.KEY_RELEASED, keyEvent -> {
if (keyEvent.getCode() == KeyCode.ENTER) {
commitEdit(getItem());
keyEvent.consume();
}
});
}
// set the text of the text field and display the graphic
@Override
public void startEdit() {
super.startEdit();
setText(null);
createTextField();
setGraphic(textField);
textField.requestFocus();
textField.selectAll();
}
// revert to text display
@Override
public void cancelEdit() {
super.cancelEdit();
setText(getItem());
setGraphic(null);
}
// See: https://gist.github.com/james-d/be5bbd6255a4640a5357
@Override
public void commitEdit(String item) {
// This block is necessary to support commit on losing focus, because the baked-in mechanism
// sets our editing state to false before we can intercept the loss of focus.
// The default commitEdit(...) method simply bails if we are not editing...
if (!isEditing() && !item.equals(getItem())) {
TableView<Item> table = getTableView();
if (table != null) {
TableColumn<Item, String> column = getTableColumn();
TableColumn.CellEditEvent<Item, String> event = new TableColumn.CellEditEvent<>(table,
new TablePosition<>(table, getIndex(), column),
TableColumn.editCommitEvent(), item);
Event.fireEvent(column, event);
}
}
super.commitEdit(item);
setText(item);
setGraphic(null);
textField = null;
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty)
setText(null);
else
setText(item);
}
}
public class IntegerSpinnerTableCell<T> extends TableCell<T, Integer> {
private final Spinner<Integer> spinner;
private final SpinnerValueFactory.IntegerSpinnerValueFactory valueFactory;
private boolean ignoreUpdate;
public IntegerSpinnerTableCell() {
spinner = new Spinner<>();
valueFactory = new SpinnerValueFactory.IntegerSpinnerValueFactory(1, Integer.MAX_VALUE, 1, 1);
spinner.setValueFactory(valueFactory);
spinner.setEditable(true);
NumberFormat format = NumberFormat.getIntegerInstance();
UnaryOperator<TextFormatter.Change> filter = c -> {
if (c.isContentChange()) {
ParsePosition parsePosition = new ParsePosition(0);
format.parse(c.getControlNewText(), parsePosition);
if (parsePosition.getIndex() == 0 || parsePosition.getIndex() < c.getControlNewText().length()) {
return null;
}
}
return c;
};
TextFormatter<Integer> intFormatter = new TextFormatter<>(new IntegerStringConverter(), 1, filter);
spinner.getEditor().setTextFormatter(intFormatter);
spinner.valueProperty().addListener((o, oldValue, newValue) -> {
if (!ignoreUpdate) {
ignoreUpdate = true;
WritableValue<Number> property = (WritableValue<Number>) getTableColumn()
.getCellObservableValue(getTableRow().getItem());
property.setValue(newValue);
ignoreUpdate = false;
}
});
spinner.getEditor().addEventFilter(KeyEvent.KEY_RELEASED, keyEvent -> {
if (keyEvent.getCode() == KeyCode.ENTER) {
commitEdit(getItem());
keyEvent.consume();
}
});
}
@Override
protected void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
ignoreUpdate = true;
setText(null);
valueFactory.setValue(item);
setGraphic(spinner);
ignoreUpdate = false;
} else {
ignoreUpdate = true;
setText(String.valueOf(item));
setGraphic(null);
ignoreUpdate = false;
}
}
}
@Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
ignoreUpdate = true;
setText(null);
setGraphic(spinner);
valueFactory.setValue(getItem());
requestEditorAndSelectText(Integer.MAX_VALUE);
ignoreUpdate = false;
}
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText(String.valueOf(getItem()));
setGraphic(null);
}
@Override
public void commitEdit(Integer newValue) {
if (!isEditing()) {
TableView<T> table = getTableView();
if (table != null) {
TableColumn<T, Integer> column = getTableColumn();
TableColumn.CellEditEvent<T, Integer> event = new TableColumn.CellEditEvent<>(table,
new TablePosition<>(table, getIndex(), column),
TableColumn.editCommitEvent(), spinner.getValue());
Event.fireEvent(column, event);
}
}
super.commitEdit(newValue);
setText(String.valueOf(newValue.intValue()));
setGraphic(null);
}
private void requestEditorAndSelectText(int maxTries) {
if (maxTries > 0) {
Platform.runLater(() -> {
if (!spinner.getEditor().isFocused()) {
spinner.getEditor().requestFocus();
spinner.getEditor().selectAll();
requestEditorAndSelectText(maxTries - 1);
}
}
);
}
}
}
private class Item {
private final StringProperty name;
private final IntegerProperty quantity;
public Item(String name) {
this.name = new SimpleStringProperty(name);
this.quantity = new SimpleIntegerProperty(1);
}
@Override
public String toString() {
return getName() + " | " + getQuantity();
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public int getQuantity() {
return quantity.get();
}
public IntegerProperty quantityProperty() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity.set(quantity);
}
}
public static void main(String[] args) {
launch();
}
}
编辑 3:为 IntegerTextFieldTableCell 添加了示例(没有 Spinner,仅 TextField):
package org.example;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.event.Event;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.converter.IntegerStringConverter;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.function.UnaryOperator;
import java.util.stream.IntStream;
public class TableWithPlainTextFieldTableCellForIntTestApp extends Application {
@Override
public void start(Stage stage) {
TableView<Item> tableView = new TableView<>();
tableView.setEditable(true);
tableView.getSelectionModel().setCellSelectionEnabled(true);
TableColumn<Item, Integer> quantityCol = new TableColumn<>("quantity");
tableView.getColumns().add(quantityCol);
quantityCol.setCellValueFactory(cellData -> cellData.getValue().quantityProperty().asObject());
quantityCol.setCellFactory(c -> new IntegerTextFieldTableCell());
// Enter edit mode on digit pressed only
tableView.setOnKeyPressed(event -> {
TablePosition<Item, ?> pos = tableView.getFocusModel().getFocusedCell();
if (pos != null && event.getCode().isDigitKey()) {
tableView.edit(pos.getRow(), pos.getTableColumn());
}
});
tableView.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
if (keyEvent.getCode() == KeyCode.ENTER) {
tableView.getSelectionModel().selectBelowCell();
keyEvent.consume();
}
});
IntStream.range(1, 5).forEach(i -> tableView.getItems().add(new Item()));
quantityCol.setMinWidth(150);
Button printBtn = new Button("print items");
printBtn.setOnAction(actionEvent -> tableView.getItems().forEach(System.out::println));
stage.setScene(new Scene(new VBox(tableView, printBtn)));
stage.show();
}
class IntegerTextFieldTableCell extends TableCell<Item, Integer> {
private TextField textField;
private void createTextField() {
textField = new TextField(getItem().toString());
// Commit value on focus lost:
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (wasFocused) {
try {
commitEdit(Integer.parseInt(textField.getText()));
} catch (NumberFormatException ignored) {
commitEdit(1);
}
}
});
// This is needed to commit last cell of the column on enter released:
// (***Alternative: Move to first cell of next column)
textField.addEventFilter(KeyEvent.KEY_RELEASED, keyEvent -> {
if (keyEvent.getCode() == KeyCode.ENTER) {
commitEdit(getItem());
keyEvent.consume();
}
});
NumberFormat format = NumberFormat.getIntegerInstance();
UnaryOperator<TextFormatter.Change> filter = c -> {
if (c.isContentChange()) {
ParsePosition parsePosition = new ParsePosition(0);
format.parse(c.getControlNewText(), parsePosition);
if (parsePosition.getIndex() == 0 || parsePosition.getIndex() < c.getControlNewText().length()) {
return null;
}
}
return c;
};
TextFormatter<Integer> intFormatter = new TextFormatter<>(new IntegerStringConverter(), 1, filter);
textField.setTextFormatter(intFormatter);
}
// set the text of the text field and display the graphic
@Override
public void startEdit() {
super.startEdit();
setText(null);
createTextField();
setGraphic(textField);
textField.requestFocus();
textField.selectAll();
}
// revert to text display
@Override
public void cancelEdit() {
super.cancelEdit();
setText(getItem().toString());
setGraphic(null);
}
// See: https://gist.github.com/james-d/be5bbd6255a4640a5357
@Override
public void commitEdit(Integer item) {
// This block is necessary to support commit on losing focus, because the baked-in mechanism
// sets our editing state to false before we can intercept the loss of focus.
// The default commitEdit(...) method simply bails if we are not editing...
if (!isEditing() && !item.equals(getItem())) {
TableView<Item> table = getTableView();
if (table != null) {
TableColumn<Item, Integer> column = getTableColumn();
TableColumn.CellEditEvent<Item, Integer> event = new TableColumn.CellEditEvent<>(table,
new TablePosition<>(table, getIndex(), column),
TableColumn.editCommitEvent(), item);
Event.fireEvent(column, event);
}
}
super.commitEdit(item);
setText(item.toString());
setGraphic(null);
textField = null;
}
@Override
protected void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty)
setText(null);
else
setText(item.toString());
}
}
class Item {
private final IntegerProperty quantity = new SimpleIntegerProperty(1);
@Override
public String toString() {
return quantity.get() + "";
}
public int getQuantity() {
return quantity.get();
}
public IntegerProperty quantityProperty() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity.set(quantity);
}
}
public static void main(String[] args) {
launch();
}
}