【问题标题】:Universal TextField for TableViewTableView 的通用文本字段
【发布时间】:2021-02-08 21:07:22
【问题描述】:

我想在 JavaFX 中为 TableCell 使用一个类 TextField,应该通用。

我可以在 TableView 中显示数据,但是更改没有到达控制器,也没有保存在数据类中。

我希望能够控制 TableView 的行为,同时正确地与我的类交互并存储数据。

目前我可以在 TableView 中看到数据类的值,并根据类中的设置对其进行编辑,但是我无法将数据导入到控制器中的数据类中。

到目前为止,这是我的方法:

控制器

columnCourse.setCellValueFactory(new PropertyValueFactory<>("CourseName"));
columnCourse.setCellFactory(i -> new CellClassTextFieldString(columnCourse, 20));
columnCourse.setOnEditCommit(e -> { System.out.println("Commit in Controller ");; });

文本字段类

public class CellClassTextFieldString<T> extends TableCell<T, String> {
    
    private final TextField textField;
    private final int maxLen;
    
    public  CellClassTextFieldString(TableColumn<T, String> stringCol, Integer maxLen) {   

        this.textField = new TextField();
        this.maxLen = maxLen;
        
        
        this.textField.setTextFormatter(new TextFormatter<>(new StringConverter<String>() {
            
            @Override
            public String toString(String object) {
                if(object == null) return "";
                
                if (object.length() > maxLen) {
                    return object.substring(0, maxLen);
                } else {
                    return object;
                }
                
            }

            @Override
            public String fromString(String string) {
               if(string == null) {
                    return "";
                } else {
                    if (string.length() > maxLen) {
                        return string.substring(0,maxLen);
                    } else {
                        return string;
                    }
                }
            }
            
        }));
        
        
       
    editableProperty().bind(stringCol.editableProperty());
    
        
        
    contentDisplayProperty().bind(Bindings
        .when(editableProperty())
                .then(ContentDisplay.GRAPHIC_ONLY)
                .otherwise(ContentDisplay.TEXT_ONLY)
    );           
        
        setListener () ;

    }   
    
    
    @Override
    protected void updateItem(String strInput, boolean empty) {
        System.out.println("updateItem");
    super.updateItem(strInput, empty);
        
    if(empty) {
            
        setText(null);
        setGraphic(null);
            
    } else {
            
        
        this.textField.setText(strInput);       
        setGraphic(this.textField);
            
        if(strInput == null) {
        setText("");
        } else {
        setText(strInput);
        }
    }   
    } 
 
    
    private void setListener () {

        this.textField.focusedProperty().addListener((observable, oldValue, newValue) -> {       
        if(newValue) {
                System.out.println("get focus");       
        } else {
                System.out.println("lost focus");
                commitEdit(textField.getText());
                
            }
    });
        
        this.textField.textProperty().addListener((observable, oldValue, newValue) -> {
        

                System.out.println("comit : " + newValue + "isEditing : " + isEditing());
                if (newValue.length() > maxLen) {
                    textField.setText(oldValue);
                }
    });   
        
        
        this.textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
            
            @Override public void handle(KeyEvent evt) {
                
                if (KeyCode.ESCAPE == evt.getCode()) {
                    System.out.println("cancel edit");
                    cancelEdit();
                }
                
                if (KeyCode.ENTER == evt.getCode() || KeyCode.TAB == evt.getCode() ) {
                    System.out.println("commit edit");
                    commitEdit(textField.getText());
                }
            }
        });
    }
}

【问题讨论】:

    标签: java javafx textfield


    【解决方案1】:

    我可以清楚地注意到的一个问题是 contentDisplayProperty 的绑定。只有在编辑时才需要 textField 吗?如果是这种情况,您需要绑定只读编辑属性而不是可编辑属性。

    contentDisplayProperty().bind(Bindings
                        .when(editingProperty())
                        .then(ContentDisplay.GRAPHIC_ONLY)
                        .otherwise(ContentDisplay.TEXT_ONLY)
                );
    

    以下是可以工作的修改后的单元格实现。 (至少它对我有用:-))

    class CellClassTextFieldString<T> extends TableCell<T, String> {
            private TextField textField;
            private final int maxLen;
    
            public CellClassTextFieldString(TableColumn<T, String> stringCol, Integer maxLen) {
                this.maxLen = maxLen;
                editableProperty().bind(stringCol.editableProperty());
                contentDisplayProperty().bind(Bindings
                        .when(editingProperty())
                        .then(ContentDisplay.GRAPHIC_ONLY)
                        .otherwise(ContentDisplay.TEXT_ONLY)
                );
            }
    
            @Override
            protected void updateItem(String strInput, boolean empty) {
                System.out.println("updateItem");
                super.updateItem(strInput, empty);
                if (empty) {
                    setText(null);
                    setGraphic(null);
                } else {
                    if (isEditing()) {
                        if (textField != null) {
                            textField.setText(getItem());
                        }
                        setGraphic(textField);
                    } else {
                        setText(getItem());
                    }
                }
            }
    
            @Override
            public final void cancelEdit() {
                super.cancelEdit();
                setText(getItem());
            }
    
            @Override
            public final void commitEdit(final String newValue) {
                super.commitEdit(newValue);
            }
    
             @Override
            public final void startEdit() {
                 super.startEdit();
                 if (textField == null) {
                     createTextField();
                 }
                 setGraphic(textField);
                 textField.setText(getItem());
                 textField.selectAll();
                 textField.requestFocus();
            }
    
            private void createTextField() {
                this.textField = new TextField();
                this.textField.setTextFormatter(new TextFormatter<>(new StringConverter<String>() {
                    @Override
                    public String toString(String object) {
                        if (object == null) return "";
    
                        if (object.length() > maxLen) {
                            return object.substring(0, maxLen);
                        } else {
                            return object;
                        }
                    }
    
                    @Override
                    public String fromString(String string) {
                        if (string == null) {
                            return "";
                        } else {
                            if (string.length() > maxLen) {
                                return string.substring(0, maxLen);
                            } else {
                                return string;
                            }
                        }
                    }
                }));
    
                this.textField.focusedProperty().addListener((observable, oldValue, newValue) -> {
                    if (!newValue) {
                        System.out.println("lost focus");
                        commitEdit(textField.getText());
                    }
                });
    
                this.textField.textProperty().addListener((observable, oldValue, newValue) -> {
                    if (newValue.length() > maxLen) {
                        textField.setText(oldValue);
                    }
                });
    
                this.textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
                    @Override
                    public void handle(KeyEvent evt) {
                        if (KeyCode.ESCAPE == evt.getCode()) {
                            System.out.println("cancel edit");
                            cancelEdit();
                        }
                        if (KeyCode.ENTER == evt.getCode() || KeyCode.TAB == evt.getCode()) {
                            System.out.println("commit edit");
                            commitEdit(textField.getText());
                        }
                    }
                });
            }
        }
    

    您可以按如下方式更新您的数据:

    columnCourse.setOnEditCommit(e -> {
                System.out.println("Commit in Controller ");
                ( e.getTableView().getItems().get(
                        e.getTablePosition().getRow())
                ).set<Name>(e.getNewValue());
            });
    

    【讨论】:

    • 您好,感谢您的解决方案。但我的文本字段不可编辑。你能帮帮我吗?
    • 我认为这很有趣。您在 TextFieldClass 中没有太大变化。仅将 TextField 创建放在一个 Method 中,并在 focusListener 中进行一些思考。但我得到不可编辑的单元格。我错过了什么吗?