gpt4 book ai didi

javafx - TableView 不会在焦点丢失事件上提交值

转载 作者:行者123 更新时间:2023-12-01 18:05:49 27 4
gpt4 key购买 nike

我想创建一个具有以下功能的表:

  • 按键编辑
  • 输入键 = 下一行
  • Tab 键 = 下一列
  • 退出键 = 取消编辑

下面是实现这些功能的代码。值(value)观应该致力于失去焦点。问题:他们没有 promise 。焦点更改事件被触发,根据控制台输出,这些值将是正确的,但最终表格单元格中的值是旧的。

有谁知道如何防止这种情况以及如何获取当前的 EditingCell 对象以便我可以手动调用提交?毕竟应该调用某种验证器,以防止在值不正确时更改焦点。

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;

public class TableViewInlineEditDemo extends Application {

private final TableView<Person> table = new TableView<>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
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"));

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

@Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setWidth(450);
stage.setHeight(550);

final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));

table.setEditable(true);

Callback<TableColumn<Person, String>, TableCell<Person, String>> cellFactory = (TableColumn<Person, String> p) -> new EditingCell();

TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
TableColumn<Person, String> emailCol = new TableColumn<>("Email");

firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(new PropertyValueFactory<>("firstName"));
firstNameCol.setCellFactory(cellFactory);
firstNameCol.setOnEditCommit((CellEditEvent<Person, String> t) -> {
((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setFirstName(t.getNewValue());
});

lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(new PropertyValueFactory<>("lastName"));
lastNameCol.setCellFactory(cellFactory);
lastNameCol.setOnEditCommit((CellEditEvent<Person, String> t) -> {
((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setLastName(t.getNewValue());
});

emailCol.setMinWidth(200);
emailCol.setCellValueFactory(new PropertyValueFactory<>("email"));
emailCol.setCellFactory(cellFactory);
emailCol.setOnEditCommit((CellEditEvent<Person, String> t) -> {
((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setEmail(t.getNewValue());
});

table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);


// edit mode on keypress
table.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent e) {

if( e.getCode() == KeyCode.TAB) { // commit should be performed implicitly via focusedProperty, but isn't
table.getSelectionModel().selectNext();
e.consume();
return;
}
else if( e.getCode() == KeyCode.ENTER) { // commit should be performed implicitly via focusedProperty, but isn't
table.getSelectionModel().selectBelowCell();
e.consume();
return;
}

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

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

}
}

}
});

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


final VBox vbox = new VBox();
vbox.getChildren().addAll(label, table);

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

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


class EditingCell extends TableCell<Person, String> {

private TextField textField;

public EditingCell() {
}

@Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.requestFocus(); // must be before selectAll() or the caret would be in wrong position
textField.selectAll();
}
}

@Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}

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

if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}

private void createTextField() {

textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);

// commit on focus lost
textField.focusedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> {

if( oldValue = true && newValue == false) {

System.out.println( "Focus lost, current value: " + textField.getText());

commitEdit();

}
});

// cancel edit on ESC
textField.addEventFilter(KeyEvent.KEY_RELEASED, e -> {

if( e.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}

});

}

private String getString() {
return getItem() == null ? "" : getItem().toString();
}

private boolean commitEdit() {
super.commitEdit(textField.getText());
return true; // TODO: add verifier and check if commit was possible
}
}

public static class Person {

private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;

private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}

public String getFirstName() {
return firstName.get();
}

public void setFirstName(String fName) {
firstName.set(fName);
}

public String getLastName() {
return lastName.get();
}

public void setLastName(String fName) {
lastName.set(fName);
}

public String getEmail() {
return email.get();
}

public void setEmail(String fName) {
email.set(fName);
}
}

}

非常感谢!

编辑:我已经缩小了范围。问题似乎是当焦点改变时,JavaFX 代码取消了编辑模式。这很糟糕。

public Cell() {
setText(null); // default to null text, to match the null item
// focusTraversable is styleable through css. Calling setFocusTraversable
// makes it look to css like the user set the value and css will not
// override. Initializing focusTraversable by calling set on the
// CssMetaData ensures that css will be able to override the value.
((StyleableProperty<Boolean>)(WritableValue<Boolean>)focusTraversableProperty()).applyStyle(null, Boolean.FALSE);
getStyleClass().addAll(DEFAULT_STYLE_CLASS);

/**
* Indicates whether or not this cell has focus. For example, a
* ListView defines zero or one cell as being the "focused" cell. This cell
* would have focused set to true.
*/
super.focusedProperty().addListener(new InvalidationListener() {
@Override public void invalidated(Observable property) {
pseudoClassStateChanged(PSEUDO_CLASS_FOCUSED, isFocused()); // TODO is this necessary??

// The user has shifted focus, so we should cancel the editing on this cell
if (!isFocused() && isEditing()) {
cancelEdit();
}
}
});

// initialize default pseudo-class state
pseudoClassStateChanged(PSEUDO_CLASS_EMPTY, true);
}

最佳答案

我很好奇并做了一些背景研究。

您面临着 JavaFX 中一个众所周知的错误问题。

背景

当您调用 commitEdit(textField.getText()) 时,它所做的第一件事是检查 isEditing() 的值,如果该值为 false,无需提交。

public void commitEdit(T newValue) {
if (! isEditing()) return;

... // Rest of the things
}

为什么返回 false?

您可能已经发现,一旦您按 TABENTER 更改您的选择,就会调用 cancelEdit()TableCell.isEditing() 设置为 false。当调用 textField 焦点属性监听器内的 commitEdit() 时,isEditing() 已经返回 false

解决方案/技巧

JavaFX 社区对此主题的讨论一直在进行。那里的人已经发布了黑客,非常欢迎您查看。

a SO thread 中显示了一个黑客攻击,这似乎完成了工作,尽管我还没有尝试过。

关于javafx - TableView 不会在焦点丢失事件上提交值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29576577/

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