Populating a JavaFX tableview when not all the properties are in the same class


Question

i got a question about populating TableViews in JavaFX. First of all, im new in javaFX so sry for my ignorance.

I made a simple example to try out how the tableview works:

public class Person{

    private StringProperty firstName, lastName;
    private IntegerProperty age;

    //Left the constructor and getters out

}

And for the controller:

public class Controller implements Initializable {

    @FXML private TableView tableView;
    @FXML private TableColumn firstNameColumn, lastNameColumn, ageColumn;

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        ObservableList<Person> data = FXCollections.observableArrayList();        
        tableView.setItems(data);

        firstNameColumn.setCellFactory(new PropertyValueFactory<Person, String>("firstName"));
        lastNameColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
        ageColumn.setCellValueFactory(new PropertyValueFactory<Person, Integer>("age"));

        data.add(new Person("Piet", "Jansen", 22));
    }
}

This works fine, but what if every person has a Name object which contains the first and lastname?

e.g:

public class Person{

    private Name name;
    private IntegerProperty age;

    //Left the constructor and getters out

}

Name class:

public class Name {

    private StringProperty firstName, lastName;

    public Name(String firstName, String lastName) {
        this.firstName = new SimpleStringProperty(firstName);
        this.lastName = new SimpleStringProperty(lastName);
    }

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

    public StringProperty firstNameProperty() {
        return firstName;
    }

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

    public StringProperty lastNameProperty() {
        return lastName;
    }
}

Thanks in advance:)

1
2
10/6/2013 1:21:04 PM

Accepted Answer

I see two ways you can achieve what you are trying to do. Option 1 would be to ensure that whenever/however the name field on the Person class is set it in turn sets the value on two other SimpleStringProperty fields representing the first and last name.

public class Person 
{
    private Name name;
    private SimpleStringProperty firstName = new SimpleStringProperty("");
    private SimpleStringProperty lastName = new SimpleStringProperty("");
    private IntegerProperty age;

    public Person(Name name) 
    {
        this.firstName.set(name.getFirstName());
        this.lastName.set(name.getLastName());
    }

    public setName(Name name) 
    {
        this.name = name;
        this.firstName.set(name.getFirstName());
        this.lastName.set(name.getLastName());
    }
}

Option 2 would use a single SimpleObjectProperty in conjunction with setting custom cell factories for each of the columns.

public class Person 
{
    private SimpleObjectProperty<Name> name = new SimpleObjectProperty<Name>();
    private IntegerProperty age;

    public Person(Name name) 
    {
        this.name.set(name);
    }
}

The cell value factories for the first and last name columns will both point to the same field (name) on your Person object but you will define separate cell factories for each column.

firstNameColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));
lastNameColumn.setCellValueFactory(new PropertyValueFactory<Person, String("name"));

firstNameColumn.setCellFactory(new Callback<TableColumn<Person,Name>,TableCell<Person,Name>>()
{
    @Override
    public TableCell<Person, Name> call(TableColumn<Person,Name> p)
    {
        final TableCell<Person,Name> cell = new TableCell<Person,Name>()
        {
            @Override
            public void updateItem(final Name item, boolean empty)
            {
                super.updateItem(item, empty);
                if (empty)
                {
                    this.setText(""); 
                }
                else
                {
                    this.setText(item.getFirstName());
                }
            }
        };
        return cell;
    }
});

Set the cell factory the same way for the lastNameColumn except in the updateItem method you will call item.getLastName() as the parameter for the setText method.

I hope this answer is helpful, let me know if it works for you!

3
10/11/2013 3:42:58 PM

By assuming that the column is not editable, you can always customize what and how to show an attribute or combination of attributes of the underlying model class in the column view rendering. For example for the given Person model class, you can show all its attributes (lastname, firstname, age etc.) in one column of the tableView. Follow the code:

TableColumn nameCol = new TableColumn("First and Last Name");
nameCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, String>, ObservableValue<String>>() {
    @Override
    public ObservableValue<String> call(TableColumn.CellDataFeatures<Person, String> p) {
        // p.getValue() returns the Person instance for a particular TableView row
        return p.getValue().getName().firstNameProperty().concat(" - ").concat(p.getValue().getName().lastNameProperty());
        // and concating maybe other attributes like: p.getValue().getOrganization().getCity().idenNumberProperty(); etc.
    }
});

If the class Name has regular attributes like

private String firstName;
private String lastName;

instead of JavaFX's SimpleStringProperty, then

TableColumn nameCol = new TableColumn("First and Last Name");
nameCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, String>, ObservableValue<String>>() {
    @Override
    public ObservableValue<String> call(TableColumn.CellDataFeatures<Person, String> p) {
        return new SimpleStringProperty(p.getValue().getName().getFirstName() + " - " + p.getValue().getName().getLastName());
    }
});

If the column is going to be editable, then you need to do more work, like parsing the "First and Last Name" column item String value and set the parts to appropriate attributes of Name class, in onEditCommit() handler for example.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon