I use JavaFx with property binding. I got a object 'Person' with the properties 'name' and age. These objects are stored in a ObservableList.
The properties are bound to labels on the gui. When I change the person in the ListBox the data also change on the right hand side.
GUI with person list:
And now it comes to my problem. I want to disply all persons on one window, like the next picture shows.
GUI with multiple persons on one view:
How can I handle this. I thought about HBox but the binding doesn't work.
FYI: Here you can find the tutorial I used. https://code.makery.ch/library/javafx-tutorial/part1/
This looks like a perfect time to use a ListView
with custom ListCell
implementations.
The sample application below shows a very basic application that displays each Person
object in a ListView
. We will provide our own ListCell
so we can control exactly how each Person
gets displayed.
I also added a profile photo just for fun :)
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
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.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.Separator;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class ListViewDetailSample extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// First, let's create our list of Persons
ObservableList<Person> persons = FXCollections.observableArrayList();
persons.addAll(
new Person("John", 34),
new Person("Cheyenne", 24),
new Person("Micah", 17),
new Person("Katelyn", 28)
);
// Create a ListView
ListView<Person> listView = new ListView<>();
// Bind our list to the ListView
listView.setItems(persons);
// Now, for the magic. We'll create our own ListCells for the ListView. This allows us to create a custom
// layout for each individual cell. For this sample, we'll include a profile picture, the name, and the age.
listView.setCellFactory(new Callback<ListView<Person>, ListCell<Person>>() {
@Override
public ListCell<Person> call(ListView<Person> param) {
return new ListCell<Person>() {
@Override
protected void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
// Set any empty cells to show nothing
if (person == null || empty) {
setText(null);
setGraphic(null);
} else {
// Here we can build our layout. We'll use a HBox for our root container
HBox cellRoot = new HBox(5);
cellRoot.setAlignment(Pos.CENTER_LEFT);
cellRoot.setPadding(new Insets(5));
// Add our profile picture
ImageView imgProfilePic = new ImageView("/sample/user.png");
imgProfilePic.setFitHeight(24);
imgProfilePic.setFitWidth(24);
cellRoot.getChildren().add(imgProfilePic);
// A simple Separator between the photo and the details
cellRoot.getChildren().add(new Separator(Orientation.VERTICAL));
// Now, create a VBox to hold the name and age
VBox vBox = new VBox(5);
vBox.setAlignment(Pos.CENTER_LEFT);
vBox.setPadding(new Insets(5));
// Add our Person details
vBox.getChildren().addAll(
new Label("Name: " + person.getName()),
new Label("Age: " + person.getAge())
);
// Add our VBox to the cellRoot
cellRoot.getChildren().add(vBox);
// Finally, set this cell to display our custom layout
setGraphic(cellRoot);
}
}
};
}
});
// Now, add our ListView to the root layout
root.getChildren().add(listView);
// Show the Stage
primaryStage.setWidth(450);
primaryStage.setHeight(400);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
// Simple Person class
class Person {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty age = new SimpleIntegerProperty();
public Person(String name, int age) {
this.name.set(name);
this.age.set(age);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public int getAge() {
return age.get();
}
public IntegerProperty ageProperty() {
return age;
}
public void setAge(int age) {
this.age.set(age);
}
}
The Result:
Without
ListView
:
If you'd prefer not to use a ListView
for this display, you can keep another list of your Person
displays and bind that to the children list of whichever container you want:
// Create a list to hold our individual Person displays
ObservableList<Node> personDisplays = FXCollections.observableArrayList();
// Now add a new PersonDisplay to the list for each Person in the personsList
persons.forEach(person -> personDisplays.add(new PersonDisplay(person)));
// Bind our personsDisplay list to the children of our root VBox
Bindings.bindContent(root.getChildren(), personDisplays);
PersonDisplay class:
class PersonDisplay extends HBox {
public PersonDisplay(Person person) {
// First, let's configure our root layout
setSpacing(5);
setAlignment(Pos.CENTER_LEFT);
setPadding(new Insets(5));
// Add our profile picture
ImageView imgProfilePic = new ImageView("/user.png");
imgProfilePic.setFitHeight(24);
imgProfilePic.setFitWidth(24);
getChildren().add(imgProfilePic);
// A simple Separator between the photo and the details
getChildren().add(new Separator(Orientation.VERTICAL));
// Now, create a VBox to hold the name and age
VBox vBox = new VBox(5);
vBox.setAlignment(Pos.CENTER_LEFT);
vBox.setPadding(new Insets(5));
// Add our Person details
vBox.getChildren().addAll(
new Label("Name: " + person.getName()),
new Label("Age: " + person.getAge())
);
// Add our VBox to the layout
getChildren().add(vBox);
}
}
The Result: