gpt4 book ai didi

javafx - 在 TableView 中输入要编辑的内容

转载 作者:行者123 更新时间:2023-12-02 03:27:37 25 4
gpt4 key购买 nike

问题

我想在输入后立即在 TableView 中切换到编辑模式。我不想先双击或按进入每个单元格,这很烦人。

我想出了以下代码。问题是它或多或少是副作用编程,我怀疑有麻烦。当您使用 KEY_RELEASED 将表格切换到编辑模式时,第一次按键会丢失。

所以你必须使用KEY_PRESSED。现在似乎一切正常,但有时您会遇到竞争条件,并且 TextField 单元格编辑器中的插入符号位于键入的文本之前而不是之后。但是,当您继续键入时,文本会正确附加到现有文本之后。

看起来不错,但从发展的角度来看,它似乎与比赛条件一团糟。

问题

有没有人有正确的方法来执行“类型编辑”功能?

代码

这是我到目前为止的代码:

public class InlineEditingTableView extends Application {

private final ObservableList<Data> data =
FXCollections.observableArrayList(
new Data(1.,5.),
new Data(2.,6.),
new Data(3.,7.),
new Data(4.,8.)
);

private TableView<Data> table;

@Override
public void start(Stage stage) {

// create edtiable table
table = new TableView<Data>();
table.setEditable(true);

// column 1 contains numbers
TableColumn<Data, Number> number1Col = new TableColumn<>("Number 1");
number1Col.setMinWidth(100);
number1Col.setCellValueFactory( cellData -> cellData.getValue().number1Property());
number1Col.setCellFactory( createNumberCellFactory());
number1Col.setOnEditCommit(new EventHandler<CellEditEvent<Data, Number>>() {
@Override
public void handle(CellEditEvent<Data, Number> t) {
System.out.println( t);
// ((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setFirstName(t.getNewValue());
}
});

// column 2 contains numbers
TableColumn<Data, Number> number2Col = new TableColumn<>("Number 2");
number2Col.setMinWidth(100);
number2Col.setCellValueFactory( cellData -> cellData.getValue().number2Property());
number2Col.setCellFactory( createNumberCellFactory());

// add columns & data to table
table.setItems(data);
table.getColumns().addAll( number1Col, number2Col);




// switch to edit mode on keypress
// this must be KeyEvent.KEY_PRESSED so that the key gets forwarded to the editing cell; it wouldn't be forwarded on KEY_RELEASED
table.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {

if( event.getCode() == KeyCode.ENTER) {
// event.consume(); // don't consume the event or else the values won't be updated;
return;
}

// switch to edit mode on keypress, but only if we aren't already in edit mode
if( table.getEditingCell() == null) {
if( event.getCode().isLetterKey() || event.getCode().isDigitKey()) {

TablePosition focusedCellPosition = table.getFocusModel().getFocusedCell();
table.edit(focusedCellPosition.getRow(), focusedCellPosition.getTableColumn());

}
}

}
});

table.addEventFilter(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {

if( event.getCode() == KeyCode.ENTER) {
table.getSelectionModel().selectBelowCell();
}
}
});

// single cell selection mode
table.getSelectionModel().setCellSelectionEnabled(true);
table.getSelectionModel().selectFirst();




// add nodes to stage
BorderPane root = new BorderPane();
root.setCenter(table);

Scene scene = new Scene( root, 800,600);
stage.setScene(scene);
stage.show();
}

/**
* Number cell factory which converts strings to numbers and vice versa.
* @return
*/
private Callback<TableColumn<Data, Number>, TableCell<Data, Number>> createNumberCellFactory() {

Callback<TableColumn<Data, Number>, TableCell<Data, Number>> factory = TextFieldTableCell.forTableColumn( new StringConverter<Number>() {

@Override
public Number fromString(String string) {
return Double.parseDouble(string);
}

@Override
public String toString(Number object) {
return object.toString();
}
});

return factory;
}

/**
* Table data container
*/
public static class Data {

private final SimpleDoubleProperty number1;
private final SimpleDoubleProperty number2;

private Data( Double number1, Double number2) {
this.number1 = new SimpleDoubleProperty(number1);
this.number2 = new SimpleDoubleProperty(number2);
}

public final DoubleProperty number1Property() {
return this.number1;
}

public final double getNumber1() {
return this.number1Property().get();
}

public final void setNumber1(final double number1) {
this.number1Property().set(number1);
}

public final DoubleProperty number2Property() {
return this.number2;
}

public final double getNumber2() {
return this.number2Property().get();
}

public final void setNumber2(final double number2) {
this.number2Property().set(number2);
}


}

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


}

最佳答案

要在单击单元格时立即进行编辑,对我来说更有意义的是 TextField s 永久显示在表格中,而不是转换到特殊的“编辑模式”并从 Label 切换到 TextField . (我认为这让所有单元格始终处于“编辑模式”,我认为这对你想要的行为是有意义的。)

如果这种 UI 满足您的要求,您可以在单元格中呈现文本字段并双向绑定(bind)文本字段的 textProperty到模型中的适当属性。这里棘手的部分是获取该属性:您必须从单元格到表格行,然后到表格行的项目,然后到您需要的属性。在任何时候,其中之一可能会发生变化(可能会更改为 null ),因此您必须处理这些可能性。

举个常见的例子:

public class Person {

// ...

public StringProperty firstNameProperty() { ... }

// etc...
}

你可以做
    TableView<Person> table = new TableView<>();
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());
firstNameCol.setCellFactory(col -> {
TableCell<Person, String> cell = new TableCell<>();
TextField textField = new TextField();

cell.graphicProperty().bind(Bindings.when(cell.emptyProperty())
.then((Node)null)
.otherwise(textField));

ChangeListener<Person> rowItemListener = (obs, oldPerson, newPerson) -> {
if (oldPerson != null) {
textField.textProperty().unbindBidirectional(((Person) oldPerson).firstNameProperty());
}
if (newPerson != null) {
textField.textProperty().bindBidirectional(((Person) newPerson).firstNameProperty());
}
};
cell.tableRowProperty().addListener((obs, oldRow, newRow) -> {
if (oldRow != null) {
oldRow.itemProperty().removeListener(rowItemListener);
if (oldRow.getItem() != null) {
textField.textProperty().unbindBidirectional(((Person) oldRow.getItem()).firstNameProperty());
}
}
if (newRow != null) {
newRow.itemProperty().addListener(rowItemListener);
if (newRow.getItem() != null) {
textField.textProperty().bindBidirectional(((Person) newRow.getItem()).firstNameProperty());
}
}
});

return cell ;
});

您可以通过使用 EasyBind 框架大大降低此处的代码复杂性,该框架提供(除其他外)通过对 null 进行适当处理来获取“属性的属性”的方法。 :
    TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());
firstNameCol.setCellFactory(col -> {
TableCell<Person, String> cell = new TableCell<>();
TextField textField = new TextField();

cell.graphicProperty().bind(Bindings.when(cell.emptyProperty())
.then((Node)null)
.otherwise(textField));

textField.textProperty().bindBidirectional(
EasyBind.monadic(cell.tableRowProperty())
.selectProperty(TableRow::itemProperty)
.selectProperty(p -> ((Person)p).firstNameProperty()));

return cell ;
});

这是一个完整的示例,我将上面的单元工厂代码分解为更通用的方法:
import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

import org.fxmisc.easybind.EasyBind;

public class LiveTableViewCell extends Application {

@Override
public void start(Stage primaryStage) {
TableView<Person> table = new TableView<>();
table.getItems().addAll(
new Person("Jacob", "Smith", "jacob.smith@example.com"),
new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
new Person("Ethan", "Williams", "ethan.williams@example.com"),
new Person("Emma", "Jones", "emma.jones@example.com"),
new Person("Michael", "Brown", "michael.brown@example.com")
);

table.getColumns().addAll(
createColumn("First Name", Person::firstNameProperty),
createColumn("Last Name", Person::lastNameProperty),
createColumn("Email", Person::emailProperty)
);

Button button = new Button("Debug");
button.setOnAction(e -> table.getItems().stream().map(p -> String.format("%s %s %s", p.getFirstName(), p.getLastName(), p.getEmail())).forEach(System.out::println));

primaryStage.setScene(new Scene(new BorderPane(table, null, null, button, null), 600, 120));
primaryStage.show();
}

private TableColumn<Person, String> createColumn(String title, Function<Person, Property<String>> property) {
TableColumn<Person, String> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));

col.setCellFactory(column -> {
TableCell<Person, String> cell = new TableCell<>();
TextField textField = new TextField();

// Example of maintaining selection behavior when text field gains
// focus. You can also call getSelectedCells().add(...) on the selection
// model if you want to maintain multiple selected cells, etc.

textField.focusedProperty().addListener((obs, wasFocused, isFocused) -> {
if (isFocused) {
cell.getTableView().getSelectionModel().select(cell.getIndex(), cell.getTableColumn());
}
});

cell.graphicProperty().bind(Bindings.when(cell.emptyProperty())
.then((Node)null)
.otherwise(textField));

// If not using EasyBind, you need the following commented-out code in place of the next statement:

// ChangeListener<Person> rowItemListener = (obs, oldPerson, newPerson) -> {
// if (oldPerson != null) {
// textField.textProperty().unbindBidirectional(property.apply((Person)oldPerson));
// }
// if (newPerson != null) {
// textField.textProperty().bindBidirectional(property.apply((Person)newPerson));
// }
// };
// cell.tableRowProperty().addListener((obs, oldRow, newRow) -> {
// if (oldRow != null) {
// oldRow.itemProperty().removeListener(rowItemListener);
// if (oldRow.getItem() != null) {
// textField.textProperty().unbindBidirectional(property.apply((Person)oldRow.getItem()));
// }
// }
// if (newRow != null) {
// newRow.itemProperty().addListener(rowItemListener);
// if (newRow.getItem() != null) {
// textField.textProperty().bindBidirectional(property.apply((Person)newRow.getItem()));
// }
// }
// });

textField.textProperty().bindBidirectional(EasyBind.monadic(cell.tableRowProperty())
.selectProperty(TableRow::itemProperty)
.selectProperty(p -> (property.apply((Person)p))));

return cell ;
});
return col ;
}

public static class Person {
private final StringProperty firstName = new SimpleStringProperty();
private final StringProperty lastName = new SimpleStringProperty();
private final StringProperty email = new SimpleStringProperty();

public Person(String firstName, String lastName, String email) {
setFirstName(firstName);
setLastName(lastName);
setEmail(email);
}

public final StringProperty firstNameProperty() {
return this.firstName;
}

public final java.lang.String getFirstName() {
return this.firstNameProperty().get();
}

public final void setFirstName(final java.lang.String firstName) {
this.firstNameProperty().set(firstName);
}

public final StringProperty lastNameProperty() {
return this.lastName;
}

public final java.lang.String getLastName() {
return this.lastNameProperty().get();
}

public final void setLastName(final java.lang.String lastName) {
this.lastNameProperty().set(lastName);
}

public final StringProperty emailProperty() {
return this.email;
}

public final java.lang.String getEmail() {
return this.emailProperty().get();
}

public final void setEmail(final java.lang.String email) {
this.emailProperty().set(email);
}


}

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

(这里令人讨厌的向下转换是因为 TableCell<S,T>.getTableRow() 返回一个原始的 TableRow 对象,而不是 TableRow<S> ,原因我一直不明白。)

关于javafx - 在 TableView 中输入要编辑的内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29586658/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com