Issue
I have a list of simple Person
objects that I am using a TableView
to display:
class Person {
private final StringProperty name = new SimpleStringProperty();
private final StringProperty emailAddress = new SimpleStringProperty();
private final StringProperty phoneNumber = new SimpleStringProperty();
public Person(String name, String emailAddress, String phoneNumber) {
this.name.set(name);
this.emailAddress.set(emailAddress);
this.phoneNumber.set(phoneNumber);
}
}
My goal in this example is to display both emailAddress
and phoneNumber
in the same TableColumn
. This goes beyond simply concatenating the values as I am implementing a custom TableCell
for this column (real-world application is more complex):
private TableCell<Person, Person> buildContactCell() {
return new TableCell<Person, Person>() {
final VBox root = new VBox();
final Label lblEmailAddress = new Label();
final Label lblPhoneNumber = new Label();
{
root.getChildren().addAll(lblEmailAddress, lblPhoneNumber);
}
@Override
protected void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (person != null && !empty) {
lblEmailAddress.setText(person.getEmailAddress());
lblPhoneNumber.setText(person.getPhoneNumber());
setGraphic(root);
} else {
setGraphic(null);
}
}
};
}
I am assuming this works fine, but I am unsure on how to configure the CellValueFactory
for this TableColumn
to accept the entire Person
object.
Is there another way of setting up the TableCell
to be able to access more than one property of Person
? The other questions I've seen on implementing multiple properties only deal with concatenating, which is not what I am looking for.
Full Code:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TableCellSample extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
root.getChildren().add(getTableView());
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Sample");
primaryStage.show();
}
private TableView<Person> getTableView() {
TableView<Person> tableView = new TableView<>();
TableColumn<Person, String> colName = new TableColumn<>("Name");
colName.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
TableColumn<Person, Person> colContact = new TableColumn<>("Contact");
colContact.setCellFactory(cell -> buildContactCell());
// **********************************************************************************************
// Need to set the CellValueFactory for colContact here
// **********************************************************************************************
tableView.getColumns().addAll(colName, colContact);
tableView.setItems(getSampleData());
return tableView;
}
private TableCell<Person, Person> buildContactCell() {
return new TableCell<Person, Person>() {
final VBox root = new VBox();
final Label lblEmailAddress = new Label();
final Label lblPhoneNumber = new Label();
{
root.getChildren().addAll(lblEmailAddress, lblPhoneNumber);
}
@Override
protected void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (person != null && !empty) {
lblEmailAddress.setText(person.getEmailAddress());
lblPhoneNumber.setText(person.getPhoneNumber());
setGraphic(root);
} else {
setGraphic(null);
}
}
};
}
private ObservableList<Person> getSampleData() {
ObservableList<Person> persons = FXCollections.observableArrayList();
persons.addAll(
new Person("Jack", "[email protected]", "123-456-7890"),
new Person("Jenny", "[email protected]", "555-867-5309"),
new Person("Jesse", "[email protected]", "846-989-9988"));
return persons;
}
}
class Person {
private final StringProperty name = new SimpleStringProperty();
private final StringProperty emailAddress = new SimpleStringProperty();
private final StringProperty phoneNumber = new SimpleStringProperty();
public Person(String name, String emailAddress, String phoneNumber) {
this.name.set(name);
this.emailAddress.set(emailAddress);
this.phoneNumber.set(phoneNumber);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public String getEmailAddress() {
return emailAddress.get();
}
public StringProperty emailAddressProperty() {
return emailAddress;
}
public String getPhoneNumber() {
return phoneNumber.get();
}
public StringProperty phoneNumberProperty() {
return phoneNumber;
}
}
Solution
Your cell value factory needs to return an ObjectProperty<Person>
containing the element for the row. You can get the row data using cellData.getValue()
in the cell value factory, where cellData
is the parameter passed to the call()
method. So:
colContact.setCellValueFactory(cellData ->
new SimpleObjectProperty<>(cellData.getValue()));
Answered By - James_D