JavaFX populate tableview with a OneToMany relatio

2019-09-08 19:02发布

to populate a table is easy when you populate it with data from one class, but what is when I want to populate it with data from a OneToMany relationship?

for example:

Class Person: Id, firstname, lastname, cars(cars have a onetomany relationship with Class Cars, so one person can have one or more cars)

Now, if a person got two cars, how can I add this to a table?

It should look something like this:

ID  |  firstname  |  lastname  |  vehicleBrand(from cars)
==============================================
1   |  jack       | jackson    |  crysler
----------------------------------------------
    |             |            |  bmw
----------------------------------------------
2   |  sally      | jackson    |  ford

If I have the observable list from Person I can populate the person data like this:

firstNameCol.setCellValueFactory(
    new PropertyValueFactory<Person,String>("firstName")
);

but something like:

vehicleBrandCol.setCellValueFactory(
new PropertyValueFactory<Cars,String>("vehicleBrand")
);

or

vehicleBrandCol.setCellValueFactory(
new PropertyValueFactory<Person,String>("vehicleBrand")
);

does not work.

Edit: The answer below works for one brand. If I try to add all I tried it by using a loop like this:

for (Car aCars : cars) {
                return new SimpleObjectProperty<>(aCars.vehicleBrand());
            }

But the loop does not loop?! (I printed the size of cars and it is not 1)

1条回答
太酷不给撩
2楼-- · 2019-09-08 19:34

This should be part of what you are looking for, you can do this:

  • Now I'm supposing you have the two classes Person and Car.
  • The class Person has an ArrayList<Car> of cars.
  • The class Person has also a method getCars() which returns this person's collection (the ArrayList) of cars.

Now to get the brand from your Car class displayed in the TableView of persons you can set the CellValueFactory as follows:

vehicleBrandCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person,String>, ObservableValue<String>>() {
            @Override
            public ObservableValue<String> call(CellDataFeatures<Person, String> param) {
                ArrayList<Car> cars = new ArrayList<Car>();
                cars = param.getValue().getCars();
                if(cars!=null && !cars.isEmpty()) {
                    return new SimpleStringProperty(cars.get(0).getBrand());
                }
            }
        }
);

The param.getValue() method returns the person passed as a parameter.

Notice that in this example I'm returning(displaying) only the first car brand from the collection of cars of the current person, you can try to adapt this example to your case.

Ok, that makes sense ^^ should I make a List of the SimpleObjectProperty and return this?

No you can't do this because you are declaring a column that should contain String objects not List objects.

What you can do is create custom cells that would allow you display all of the Car brands of a each Person instance, you can take a look at this example to get a grasp of Cell rendering, and here is my example that could help you in your case:

First set the type of the content of cells in vehicleBrandCol to ArrayList<String>:

private TableColumn<Person, ArrayList<Car>> vehicleBrandCol;

Then set the CellValueFactory as follows:

vehicleBrandCol.setCellValueFactory(new PropertyValueFactory<Person, ArrayList<Car>>("cars"));

and then you'll need custom cells to display data (Cars) contained within this ArrayList, so set the CellFactory as follows:

vehicleBrandCol.setCellFactory(column->{
            return new TableCell<Person, ArrayList<Car>>() {
                @Override
                protected void updateItem(ArrayList<Car> item, boolean empty) {
                    super.updateItem(item, empty);
                    if(item==null || empty) {
                        setGraphic(null);
                    } else {
                        VBox vbox = new VBox();
                        for(Car car : item) {
                            Label lbl = new Label(car.getBrand());
                            vbox.getChildren().add(lbl);
                        }
                        setGraphic(vbox);
                    }
                }
            };
        });

Notice that I'm creating a VBox and using Labels, you can use whatever Node you like, for example you can use a ListView, or a GridPane and set them as graphic for the Cell.

查看更多
登录 后发表回答