I want to have a JavaFX ListView of Person objects. I want the list to display only the name and allow the name to be edited. It should also preserve the other fields in each object after committing an edit to the name. How would you do this idiomatically in JavaFX?
I have the following code, which works, but it's kind of wonky because it has a StringConverter that converts one way from Person to a String of the person's name then doesn't do the reverse conversion and instead relies on the list cell commitEdit method to take a string of the name and set it on the appropriate person.
Here's the code:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
stage.setTitle("My Custom List View");
ObservableList<Person> people = FXCollections.observableArrayList(
new Person("John Doe", "123 New York"),
new Person("Jane Doe", "456 San Francisco")
);
ListView<Person> listView = new ListView();
listView.setCellFactory(new CustomCellFactory());
listView.setEditable(true);
listView.setItems(people);
Scene scene = new Scene(listView,400,300);
stage.setScene(scene);
stage.show();
}
public static class CustomCellFactory implements Callback<ListView<Person>,ListCell<Person>> {
@Override
public ListCell<Person> call(ListView param) {
TextFieldListCell<Person> cell = new TextFieldListCell() {
@Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
System.out.println("updating item: "+item.toString());
setText(((Person) item).getName());
} else {
setText(null);
}
}
@Override
public void commitEdit(Object newName) {
((Person)getItem()).setName((String)newName);
super.commitEdit(getItem());
}
};
cell.setConverter(new StringConverter() {
@Override
public String toString(Object person) {
return ((Person)person).getName();
}
@Override
public Object fromString(String string) {
return string;
}
});
return cell;
}
}
public static class Person {
private String name;
private String address;
public Person(String name, String address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String toString() {
return name+" at "+address;
}
}
}