1

UPDATE: This qustion moved to TableColumn should only show one specific value of a complex data type because it's more specific

I want to populate a table with a complex data type Person containing name, id and a List<Person>. At the moment I get a table with the correct name, id and a third column with the whole information of other Persons but it should only show the name of the Personss. Is there any way that my third column shows only the Person.getName() values?

Keywords, solutions welcome! Thank you very much!

Edit: Code

Person.class

public class Person {
    String id;
    String name;
    List<Person> friends;

public String getId() {...}
public String getName() {....}
public List<Person> getFriends {...}
}

TableView.class

public TableView<Person> createTable() {
    TableColumn<Person, String> firstCol = new TableColumn<>("ID");
        TableColumn<Person, String> secondCol = new TableColumn<>("Name");
        TableColumn<Person, List<Person> thirdCol = new TableColumn<>("Friends");

        PropertyValueFactory<Person, String> firstColFactory = new PropertyValueFactory<>(
            "id");
        PropertyValueFactory<Person, String> secondColFactory = new PropertyValueFactory<>(
            "name");
        PropertyValueFactory<Person, List<Person>> thirdColFactory = new PropertyValueFactory<>(
            "friends");

    firstCol.setCellValueFactory(firstColFactory);
    secondCol.setCellValueFactory(secondColFactory);
    thirdCol.setCellValueFactory(thirdColFactory);

    myTableView.getColumns().addAll(firstCol, secondCol, thirdCol);
}

So if I fill the table id and name-colums contain the correct name and the third column (with List<Person>) shows [Person [id1, John, ...]...]]. And now I want to ask if theres a possibility that the third column only displays the id or name without manipulating the data?

Community
  • 1
  • 1
cmtjk
  • 359
  • 2
  • 12

2 Answers2

7

As an alternative to Uluk's solution, consider setting the cell factory on the column, instead of the cell value factory. The choice between these depends on how you regard the relationship between the column and the model (Person). If you consider the list of names to be the data displayed by the column, then Uluk's solution is the way to go. If you consider the list of Person objects to be the data, which are displayed by their names, then this will be the more intuitive option:

TableColumn<Person, List<Person> thirdCol = new TableColumn<>("Friends");
PropertyValueFactory<Person, List<Person>> thirdColFactory = new PropertyValueFactory<>("friends");
thirdCol.setCellValueFactory(thirdColFactory);

thirdCol.setCellFactory(col -> new TableCell<Person, List<Person>>() {
    @Override
    public void updateItem(List<Person> friends, boolean empty) {
        super.updateItem(friends, empty);
        if (empty) {
            setText(null);
        } else {
            setText(friends.stream().map(Person::getName)
                .collect(Collectors.joining(", ")));
        }
    }
});

This makes it easy to change the way the list is displayed in that column, e.g.:

thirdCol.setCellFactory( col -> {
    ListView<Person> listView = new ListView<>();
    listView.setCellFactory(lv -> new ListCell<Person>() {
        @Override
        public void updateItem(Person person, boolean empty) {
            super.updateItem(person, empty);
            if (empty) {
                setText(null);
            } else {
                setText(person.getName());
            }
        }
    });
    return new TableCell<Person, List<Person>>() {
        @Override
        public void updateItem(List<Person> friends, boolean empty) {
            super.updateItem(friends, empty);
            if (empty) {
                setGraphic(null);
            } else {
                listView.getItems().setAll(friends);
                setGraphic(listView);
            }
        }
    };
});
James_D
  • 201,275
  • 16
  • 291
  • 322
  • Thank you very much for that solution! It works for me and I'll try to understand this ;) – cmtjk Jun 30 '15 at 12:15
5

You may define cell value factory with the long expanded version where you control which fields to show:

thirdColFactory.setCellValueFactory(
  ( TableColumn.CellDataFeatures<Person, String> p ) ->
{
    List<Person> friends = p.getValue().getFriends();
    String val = friends
                 .stream()
                 .map( item -> item.getName() )
                 .reduce( "", ( acc, item ) -> acc + ", " + item );
    return new ReadOnlyStringWrapper( val );
});
Uluk Biy
  • 48,655
  • 13
  • 146
  • 153
  • 2
    Instead of `reduce(...)`, use `collect(Collectors.joining(", "))`, which will be more efficient (no string concatenation in a loop...). – James_D Jun 30 '15 at 11:26
  • @Uluk Thanks for your answer. Didn't work for me but based on this James_D got an solution. – cmtjk Jun 30 '15 at 12:00
  • Could anyone explain me why I get this error with the code above: "incompatible types: incompatible parameter types in lambda expression"? – runefist May 27 '19 at 14:29