Элементы JavaFX TableView не изменяются после изменений в таблице в пользовательском интерфейсе

У меня есть TableView, который я заполняю объектами MappingItem. Цель состоит в том, чтобы создать сопоставление между исходными полями Excel и полями базы данных.

В TableView у меня есть два столбца. Один из <MappingItem, String> и представляет собой заголовок Excel. Другой имеет <MappingItem, GoldplusField> и представляет собой поле базы данных. Ячейки второго столбца - ComboBoxTableCell, в которых есть список полей из моей БД.

Проблема в том, что после того, как я изменил выбор в поле со списком второго столбца, MappingItem не обновляется моим выбором. Я попытался получить выбранную ячейку и извлечь элемент, но всегда получаю пустые ссылки.

Это пользовательский интерфейс: введите здесь описание изображения

Это пример кода:

package tableviewexample;

import javafx.application.Application;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.value.ObservableValue;
import javafx.collections.*;
import javafx.event.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;

public class TableViewExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        TableView<MappingItem> table = new TableView<>();

        // FIRST COLUMN
        TableColumn<MappingItem, String> colA = new TableColumn<>("Excel Column");        

        colA.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<MappingItem, String>, ObservableValue<String>> () {
            @Override
            public ObservableValue<String> call(TableColumn.CellDataFeatures<MappingItem, String> param) {
                return new ReadOnlyObjectWrapper(param.getValue().getExcelColumnName());
            }            
        });   

        //SECOND COLUMN
        TableColumn<MappingItem, GoldplusField> colB = new TableColumn<>("Database Field Column");
        colB.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<MappingItem, GoldplusField>, ObservableValue<GoldplusField>> () {
            @Override
            public ObservableValue<GoldplusField> call(TableColumn.CellDataFeatures<MappingItem, GoldplusField> param) {
                return new ReadOnlyObjectWrapper(param.getValue().getGpField());
            }            
        });

        GoldplusField gp1 = new GoldplusField("T1", "fName", "First Name");
        GoldplusField gp2 = new GoldplusField("T1", "phn", "Phone");

        ObservableList<GoldplusField> fieldsList = FXCollections.observableArrayList(gp1, gp2);
        colB.setCellFactory(ComboBoxTableCell.forTableColumn(new FieldToStringConvertor(), fieldsList));         

        colB.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<MappingItem, GoldplusField>>() {

            public void handle(TableColumn.CellEditEvent<MappingItem, GoldplusField> e) {

                GoldplusField gpf = colB.getCellData(table.getFocusModel().getFocusedItem());
                System.out.println(gpf.getGpName());

                MappingItem item = table.getSelectionModel().getSelectedItem(); 
                System.out.println(item.getGpField().getGpName());

            }
        });

        table.setEditable(true);
        table.getColumns().addAll(colA, colB);

        MappingItem mi1 = new MappingItem("name");
        MappingItem mi2 = new MappingItem("phone");
        ObservableList<MappingItem> miList = FXCollections.observableArrayList(mi1, mi2);

        table.setItems(miList);

        StackPane root = new StackPane();
        root.getChildren().add(table);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

    class FieldToStringConvertor extends StringConverter<GoldplusField> {

        @Override
        public String toString(GoldplusField object) {

            if (object != null)
                return object.getGpName();
            else
                return "";
        }

        @Override
        public GoldplusField fromString(String string) {
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
        }        
    }

    class MappingItem {
        private String excelColumnName;
        private GoldplusField gpField;

        public String getExcelColumnName() { return excelColumnName; }

        public void setExcelColumnName(String excelColumnName) { this.excelColumnName = excelColumnName; }    

        public GoldplusField getGpField() { return gpField; }

        public void setGpField(GoldplusField gpField) { this.gpField = gpField; }

        public MappingItem(String columnName) {
            this.excelColumnName= columnName;
        }    
        public MappingItem(GoldplusField gpField) {
            this.gpField = gpField;
        }  
        public MappingItem(String columnName, GoldplusField gpField) {
            this.excelColumnName = columnName;
            this.gpField = gpField;
        }       
    }    

    class GoldplusField {
        private String table;
        private String dbName;
        private String gpName;

        public String getDbName() { return dbName; }

        public String getGpName() { return gpName; }

        public String getTable() { return table; }

        public void setDbName(String dbName) { this.dbName = dbName; }

        public void setGpName(String gpName) { this.gpName = gpName; }

        public void setTable(String table) { this.table = table; }

        public GoldplusField(String table, String dbName, String gpName) {
            this.dbName = dbName;
            this.gpName = gpName;
            this.table = table;
        }
    } 

}

person Ido Gal    schedule 28.09.2015    source источник
comment
Вы пытались использовать ComboBoxTableCell.forTableColumn(...)   -  person Uluk Biy    schedule 28.09.2015
comment
Вы также установили setOnEditCommit(...) для столбцов таблицы?   -  person Uluk Biy    schedule 28.09.2015
comment
трудно сказать без SSCCE (ага, время для вашей любимой поисковой машины :-) - на первый взгляд я не вижу никаких записываемых свойств в ваших фрагментах, так как вы ожидаете, что изменение в столбце gp повлияет на любые другие?   -  person kleopatra    schedule 28.09.2015
comment
@UlukBiy ты опередил меня в другой возможности :-)   -  person kleopatra    schedule 28.09.2015
comment
@kleopatra - у меня есть сеттеры. Поменял на SSCCE, спасибо.   -  person Ido Gal    schedule 28.09.2015
comment
@UlukBiy - Тоже пробовал. Проверьте SSCCE. Я получаю исключение NullReferenceException при попытке доступа к объекту в OnEditCommit.   -  person Ido Gal    schedule 28.09.2015
comment
спасибо за SSCCE :-) Хотите знать, что вы делаете ожидаете? И зачем обращаться к tableView для выбранного/сфокусированного элемента - у editEvent есть вся информация?   -  person kleopatra    schedule 28.09.2015
comment
@UlukBiy, спасибо.   -  person Ido Gal    schedule 28.09.2015
comment
@kleopatra, ты был прав. У меня не было нормальных сеттеров, наверное. Изменил все на правильные свойства JavaFx, и теперь все в порядке. Благодарю вас!   -  person Ido Gal    schedule 28.09.2015
comment
@IdoGal Добро пожаловать! :)   -  person Uluk Biy    schedule 28.09.2015


Ответы (1)


ХОРОШО. Как уже упоминалось, проблема, вероятно, заключалась в том, что свойства не были доступны для записи.

В итоге я изменил свойства своих объектов на свойства JavaFX. Затем я установил PropertyValueFactory для каждого из них и передал его в CellValueFactory столбца.

Спасибо.

private void populateSourceColumnsColumn() {

    ArrayList<MappingItem> items = new ArrayList<> ();

    ArrayList<String> headers = SheetHelper.getTableHeadersAsString(sheet, true);

    for (String header : headers) {
        items.add(new MappingItem(header) );
    }

    ObservableList<MappingItem> itemsList = FXCollections.observableArrayList(items);
    mappingTable.setItems(itemsList);         

    // First Column   
    PropertyValueFactory<MappingItem, String> fNameCellValueFactory = new PropertyValueFactory<>("excelColumnName");
    inputColumnsColumn.setCellValueFactory(fNameCellValueFactory);

    // Second Column 
    PropertyValueFactory<MappingItem, GoldplusField> gpFieldCellValueFactory = new PropertyValueFactory<>("gpField");
    goldplusFieldsColumn.setCellValueFactory(gpFieldCellValueFactory);

    GoldplusDatabase gpDb = new GoldplusDatabase(DatasourceContext.INSTANCE.getDataSource());
    ObservableList<GoldplusField> fieldsList = FXCollections.observableArrayList(gpDb.getContactFields());
    goldplusFieldsColumn.setCellFactory(ComboBoxTableCell.forTableColumn(new FieldToStringConvertor(), fieldsList));        

}

public class MappingItem {
    private StringProperty excelColumnName = new SimpleStringProperty(this, "excelColumnName");
    private ObjectProperty<GoldplusField> gpField = new SimpleObjectProperty<GoldplusField>(this, "gpField");

    public String getExcelColumnName() {
        return excelColumnName.get();
    }
    public void setExcelColumnName(String excelColumnName) {
        this.excelColumnName.set(excelColumnName);
    }    
    public StringProperty excelColumnNameProperty() {
        return excelColumnName;
    }
    public GoldplusField getGpField() {
        return gpField.get();
    }
    public void setGpField(GoldplusField gpField) {
        this.gpField.set(gpField);
    }    
    public ObjectProperty gpFieldProperty() {
        return this.gpField;
    }    
    public MappingItem() {
        super();
    }    
    public MappingItem(String columnName) {
        this.excelColumnName.set(columnName);
    }    
    public MappingItem(GoldplusField gpField) {
        this.gpField.set(gpField);
    } 
    public MappingItem(String columnName, GoldplusField gpField) {        
        this.excelColumnName.set(columnName);
        this.gpField.set(gpField);
    }       
}

public class GoldplusField {

    private StringProperty table = new SimpleStringProperty(this, "table");
    private StringProperty dbName = new SimpleStringProperty(this, "dbName");
    private StringProperty gpName = new SimpleStringProperty(this, "gpName");

    public String getDbName() {
        return dbName.get();
    }
    public String getGpName() {
        return gpName.get();
    }
    public String getTable() {
        return table.get();
    }
    public void setDbName(String dbName) {
        this.dbName.set(dbName);
    }
    public void setGpName(String gpName) {
        this.gpName.set(gpName);
    }
    public void setTable(String table) {
        this.table.set(table);
    }    
    public StringProperty tableProperty() {
        return this.table;
    }    
    public StringProperty gpNameProperty() {
        return this.gpName;
    }       
    public StringProperty dbNameProperty() {
        return this.dbName;
    }    
    public GoldplusField(String table, String dbName, String gpName) {

        this.dbName.set(dbName);
        this.gpName.set(gpName);
        this.table.set(table);
    }
}
person Ido Gal    schedule 28.09.2015
comment
выглядит разумно - рад, что вы нашли решение :-) - person kleopatra; 28.09.2015