Adding new row with button to javafx tableview - java

I have defined a tableview in fxml. It is something like the following:
SNO Name DOB Action
The Action column would contain buttons in each row with text "delete". I have two questions:
How do I add this delete button to each new row last cell in javafx?
How do I get the index of the row whose delete button is clicked?(So that I can delete the row or do other event handling work)

I think this example use for your project just go through it and implement in your project.` package checkboxdemo;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
*
* #author reegan
*/
public class Checkboxdemo extends Application {
static int i = 0;
#Override
public void start(Stage primaryStage) {
CheckBox checkBox = new CheckBox();
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue ov,
Boolean old_val, Boolean new_val) {
if (ov.getValue() == true) {
i = i + 1;
System.out.println(i);
}
}
});
TableView tableView = new TableView();
TableColumn column = new TableColumn("check");
TableColumn column1 = new TableColumn("Name");
tableView.getColumns().addAll(column,column1);
VBox root = new VBox();
root.getChildren().addAll(checkBox,tableView);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
* main() serves only as fallback in case the application can not be
* launched through deployment artifacts, e.g., in IDEs with limited FX
* support. NetBeans ignores main().
*
* #param args the command line arguments
*/
// public static void main(String[] args) {
// launch(args);
// }
}
package checkboxdemo;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
private StringProperty firstName;
private StringProperty lastName;
public Person(String firstName, String lastName) {
setFirstName(firstName);
setLastName(lastName);
}
public final void setFirstName(String value) { firstNameProperty().set(value); }
public final void setLastName(String value) { lastNameProperty().set(value); }
public String getFirstName() { return firstNameProperty().get(); }
public String getLastName() { return lastNameProperty().get(); }
public StringProperty firstNameProperty() {
if (firstName == null) firstName = new SimpleStringProperty(this, "firstName");
return firstName;
}
public StringProperty lastNameProperty() {
if (lastName == null) lastName = new SimpleStringProperty(this, "lastName");
return lastName;
}
}
and in TableView add the button in cell
package checkboxdemo;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.event.*;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.stage.*;
import javafx.util.Callback;
public class TableViewWithAddButtonExample extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(final Stage stage) {
stage.setTitle("People");
// stage.getIcons().add(new Image("http://icons.iconarchive.com/icons/icons-land/vista-people/72/Historical-Viking-Female-icon.png")); // icon license: Linkware (Backlink to http://www.icons-land.com required)
// create a table.
final TableView<Person> table = new TableView<>(
FXCollections.observableArrayList(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams"),
new Person("Emma", "Jones"),
new Person("Michael", "Brown")
)
);
// define the table columns.
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
TableColumn<Person, Boolean> actionCol = new TableColumn<>("Action");
actionCol.setSortable(false);
// define a simple boolean cell value for the action column so that the column will only be shown for non-empty rows.
actionCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, Boolean>, ObservableValue<Boolean>>() {
#Override public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Person, Boolean> features) {
return new SimpleBooleanProperty(features.getValue() != null);
}
});
// create a cell value factory with an add button for each row in the table.
actionCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
#Override public TableCell<Person, Boolean> call(TableColumn<Person, Boolean> personBooleanTableColumn) {
return new AddPersonCell(stage, table);
}
});
table.getColumns().setAll(firstNameCol, lastNameCol, actionCol);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
stage.setScene(new Scene(table));
stage.show();
}
/** A table cell containing a button for adding a new person. */
private class AddPersonCell extends TableCell<Person, Boolean> {
// a button for adding a new person.
final Button addButton = new Button("Add");
// pads and centers the add button in the cell.
final StackPane paddedButton = new StackPane();
// records the y pos of the last button press so that the add person dialog can be shown next to the cell.
final DoubleProperty buttonY = new SimpleDoubleProperty();
/**
* AddPersonCell constructor
* #param stage the stage in which the table is placed.
* #param table the table to which a new person can be added.
*/
AddPersonCell(final Stage stage, final TableView table) {
paddedButton.setPadding(new Insets(3));
paddedButton.getChildren().add(addButton);
addButton.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
buttonY.set(mouseEvent.getScreenY());
}
});
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
showAddPersonDialog(stage, table, buttonY.get());
table.getSelectionModel().select(getTableRow().getIndex());
}
});
}
/** places an add button in the row only if the row is not empty. */
#Override protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(paddedButton);
}
}
}
/**
* shows a dialog which displays a UI for adding a person to a table.
* #param parent a parent stage to which this dialog will be modal and placed next to.
* #param table the table to which a person is to be added.
* #param y the y position of the top left corner of the dialog.
*/
private void showAddPersonDialog(Stage parent, final TableView<Person> table, double y) {
// initialize the dialog.
final Stage dialog = new Stage();
dialog.setTitle("New Person");
dialog.initOwner(parent);
dialog.initModality(Modality.WINDOW_MODAL);
dialog.initStyle(StageStyle.UTILITY);
dialog.setX(parent.getX() + parent.getWidth());
dialog.setY(y);
// create a grid for the data entry.
GridPane grid = new GridPane();
final TextField firstNameField = new TextField();
final TextField lastNameField = new TextField();
grid.addRow(0, new Label("First Name"), firstNameField);
grid.addRow(1, new Label("Last Name"), lastNameField);
grid.setHgap(10);
grid.setVgap(10);
GridPane.setHgrow(firstNameField, Priority.ALWAYS);
GridPane.setHgrow(lastNameField, Priority.ALWAYS);
// create action buttons for the dialog.
Button ok = new Button("OK");
ok.setDefaultButton(true);
Button cancel = new Button("Cancel");
cancel.setCancelButton(true);
// only enable the ok button when there has been some text entered.
ok.disableProperty().bind(firstNameField.textProperty().isEqualTo("").or(lastNameField.textProperty().isEqualTo("")));
// add action handlers for the dialog buttons.
ok.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
int nextIndex = table.getSelectionModel().getSelectedIndex() + 1;
table.getItems().add(nextIndex, new Person(firstNameField.getText(), lastNameField.getText()));
table.getSelectionModel().select(nextIndex);
dialog.close();
}
});
cancel.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
dialog.close();
}
});
// layout the dialog.
HBox buttons = HBoxBuilder.create().spacing(10).children(ok, cancel).alignment(Pos.CENTER_RIGHT).build();
VBox layout = new VBox(10);
layout.getChildren().addAll(grid, buttons);
layout.setPadding(new Insets(5));
dialog.setScene(new Scene(layout));
dialog.show();
}
}

Related

how to generate button(picture) based on checkboxes selection in javafx

My question is how to generate buttons, set with car pictures, based on the checkboxes and/or radio buttons selected by a user in javafx?
I'm simulating a car dealership website with car pictures. The user should be able to filter the pictures displayed by clicking checkboxes and/or radio buttons selection.
I'm first creating all the picture buttons with a for each loop. I could use if and if/else statements to filter through the pictures but there would be duplicates. I've heard of observablelist but I haven't learned those yet.
Can someone help me out with this one please? Thank you!
ArrayList<Car> cars;
for (Car r : cars)
{
for (int i = 0; i < SIZE; i++)
{
// create buttons and set car pictures
carButton[i] = new Button();
carButton[i].setId(String.format("%d", i));
carButton[i].setGraphic(cars.get(i).getCarPicture());
}
}
Instead of using an ArrayList for your cars, I recommend using an ObservableList:
ObservableList<Car> carsList = FXCollections.observableArrayList<>();
An ObservableList allows you to listen for changes and respond accordingly. For example, when a new Car is added to the list, you could trigger an event that automatically adds a new Button to your scene.
Here is a short demo application that shows how this would work. I did comment the code below as well and many of the concepts being used may be beyond your level, but it's one method, at least.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.ImageView;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// Create an ObservableList to hold our Cars
ObservableList<Car> carsList = FXCollections.observableArrayList();
// For our sample, let's use a FlowPane to display all of our buttons. We will add new buttons to this FlowPane
// automatically as new Cars are added to carsList
FlowPane flowPane = new FlowPane();
flowPane.setHgap(10);
flowPane.setVgap(5);
flowPane.setAlignment(Pos.TOP_CENTER);
// Create a ListChangeListener for our carsList. This allows us to perform some actions whenever an item is added
// to or removed from the list. For our example, we will only do something when a new Car is added.
carsList.addListener(new ListChangeListener<Car>() {
#Override
public void onChanged(Change<? extends Car> c) {
System.out.println(carsList.size());
// Get the first change
c.next();
// If an item was added to the list...
if (c.wasAdded()) {
// Create a new button and add it to the FlowPane
// The Change (c) provides access to a List of items that were added during this change. Since we
// are only going to add one Car at a time, we only need to get the first item from the AddedSubList
Button button = new Button(c.getAddedSubList().get(0).getName());
button.setGraphic(c.getAddedSubList().get(0).getIcon());
button.setOnAction(event -> {
// The code to be executed when this button is clicked goes here
});
// Add the button to our FlowPane
flowPane.getChildren().add(button);
}
}
});
// Now we need a Button that will add a new car to the List
Button button = new Button("Add Car");
button.setOnAction(event -> {
// We'll just add a random car to the carsList
carsList.add(new Car("Car #" + (carsList.size() + 1), new ImageView("icon.png")));
});
// Add our FlowPane and Button to the root layout
root.getChildren().addAll(button, flowPane);
primaryStage.setScene(new Scene(root, 550, 250));
primaryStage.show();
}
}
class Car {
private final String name;
private final ImageView icon;
public Car(String name, ImageView icon) {
this.name = name;
this.icon = icon;
}
public String getName() {
return name;
}
public ImageView getIcon() {
return icon;
}
}
The Results: (after clicking the "Add Car" button a few times)
This is a terrible implementation but It will give you some ideas on how to do things. You need to research FilteredList, ListView, and Predicate. This implementation does not handle more than one CheckBox at a time. It will only display the last CheckBox action.
CarList
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
*
* #author Sedrick
*/
public class CarList extends Application {
#Override
public void start(Stage primaryStage) {
List<Car> cars = new ArrayList();
cars.add(new Car("Honda", "2004"));
cars.add(new Car("Ford", "2005"));
cars.add(new Car("Ford", "2004"));
cars.add(new Car("Honda", "2005"));
cars.add(new Car("Toyota", "2004"));
cars.add(new Car("Cadillac", "2005"));
ListView<Car> view = new ListView();
view.setCellFactory((ListView<Car> param) -> {
ListCell<Car> cell = new ListCell<Car>() {
CarView carView = new CarView();
#Override
protected void updateItem(Car item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText("");
carView.setMake(item.getMake());
carView.setModel(item.getModel());
carView.setImageView(item.getUrl());
setGraphic(carView);
} else {
setText("");
setGraphic(null);
}
}
};
return cell;
});
ObservableList<Car> data = FXCollections.observableArrayList(cars);
FilteredList<Car> filteredList = new FilteredList(data);
view.setItems(filteredList);
HBox.setHgrow(view, Priority.ALWAYS);
CheckBox checkBox = new CheckBox("Honda");
checkBox.selectedProperty().addListener((observable, oldValue, newValue) -> {
if(newValue)
{
filteredList.setPredicate((item) -> {
return item.getMake().equals("Honda");
});
}
else{
filteredList.setPredicate((item) -> {
return true;
});
}
});
CheckBox checkBox2 = new CheckBox("Ford");
checkBox2.selectedProperty().addListener((observable, oldValue, newValue) -> {
if(newValue)
{
filteredList.setPredicate((item) -> {
return item.getMake().equals("Ford");
});
}
else{
filteredList.setPredicate((item) -> {
return true;
});
}
});
CheckBox checkBox3 = new CheckBox("2004");
checkBox3.selectedProperty().addListener((observable, oldValue, newValue) -> {
if(newValue)
{
filteredList.setPredicate((item) -> {
return item.getModel().equals("2004");
});
}
else{
filteredList.setPredicate((item) -> {
return true;
});
}
});
CheckBox checkBox4 = new CheckBox("2005");
checkBox4.selectedProperty().addListener((observable, oldValue, newValue) -> {
if(newValue)
{
filteredList.setPredicate((item) -> {
return item.getModel().equals("2005");
});
}
else{
filteredList.setPredicate((item) -> {
return true;
});
}
});
VBox leftPanel = new VBox(checkBox, checkBox2, checkBox3, checkBox4);
HBox root = new HBox(leftPanel, view);
Scene scene = new Scene(root, 625, 500);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
CarView
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
/**
*
* #author Sedrick
*/
final public class CarView extends HBox{
Label make = new Label();
Label model = new Label();
ImageView imageView = new ImageView();
public CarView(String make, String model, String url) {
this.make.setText(make);
this.model.setText(model);
HBox row1 = new HBox(new Label("Make: "), this.make);
HBox row2 = new HBox(new Label("Model: "), this.model);
VBox vbox = new VBox(row1, row2);
vbox.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
StackPane stackPane1 = new StackPane(vbox);
HBox.setHgrow(stackPane1, Priority.ALWAYS);
Image image = new Image(url);
this.imageView.setImage(image);
this.imageView.setFitHeight(100);
this.imageView.setFitWidth(200);
StackPane stackPane2 = new StackPane(this.imageView);
stackPane2.setStyle("-fx-background-color: yellow");
getChildren().addAll(stackPane1, stackPane2);
setPrefSize(500, 125);
}
public CarView()
{
HBox row1 = new HBox(new Label("Make: "), this.make);
HBox row2 = new HBox(new Label("Model: "), this.model);
VBox vbox = new VBox(row1, row2);
vbox.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
StackPane stackPane1 = new StackPane(vbox);
HBox.setHgrow(stackPane1, Priority.ALWAYS);
this.imageView.setFitHeight(100);
this.imageView.setFitWidth(200);
StackPane stackPane2 = new StackPane(this.imageView);
stackPane2.setStyle("-fx-background-color: yellow");
getChildren().addAll(stackPane1, stackPane2);
setPrefSize(500, 125);
}
public void setImageView(String url) {
Image image = new Image(url);
this.imageView.setImage(image);
}
public void setMake(String make) {
this.make.setText(make);
}
public void setModel(String model)
{
this.model.setText(model);
}
}
Car
/**
*
* #author Sedrick
*/
public class Car {
private String make;
private String model;
private String url = "https://cdn.pixabay.com/photo/2012/05/29/00/43/car-49278_960_720.jpg";
public Car(String make, String model) {
this.make = make;
this.model = model;
}
public String getMake() {
return make;
}
public String getModel() {
return model;
}
public String getUrl()
{
return url;
}
public void setMake(String make) {
this.make = make;
}
public void setModel(String model) {
this.model = model;
}
}

Javafx TableView change Font of an Column programmatically NOT-CSS [duplicate]

This question already has answers here:
Setting font color of JavaFX TableView Cells?
(5 answers)
Closed 2 years ago.
I want to change how the all column's text looks like which is a SimpleStringProperty. I want to change font style,font name, font size etc... Below my creation of TableView
TableView<User> statisticsTable = new TableView<>();
//statisticsTable.setPrefHeight(DefaultValues.TE);
TableColumn nameCol = new TableColumn("Name");
nameCol.setMinWidth(240);
nameCol.setCellValueFactory(
new PropertyValueFactory<User, String>("fullName"));
nameCol.setResizable(false);
TableColumn todayTicketsCol = new TableColumn("Today Assigned Tickets");
todayTicketsCol.setMinWidth(160);
todayTicketsCol.setCellValueFactory(
new PropertyValueFactory<User, Integer>("totalOnTechnicalStudies"));
todayTicketsCol.setResizable(false);
TableColumn totalTechnicalStudiesCol = new TableColumn("Total Technical Studies");
totalTechnicalStudiesCol.setMinWidth(160);
totalTechnicalStudiesCol.setCellValueFactory(
new PropertyValueFactory<User, Integer>("totalAssignedTicket"));
totalTechnicalStudiesCol.setResizable(false);
usersForStatistics = FXCollections.observableArrayList(usersList);
statisticsTable.getColumns().addAll(nameCol,todayTicketsCol,totalTechnicalStudiesCol);
statisticsTable.setItems(usersForStatistics);
Here is a sample app that demonstrates how to do this.
This example uses setTextFill() and setFont() inside the setCellFactory method.
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;
public class Main extends Application {
private final TableView<Person> table = new TableView<>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(new Person("A", "B"));
final HBox hb = new HBox();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setWidth(450);
stage.setHeight(550);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<>("firstName"));
//Newly added code
firstNameCol.setCellFactory(new Callback<TableColumn, TableCell>() {
#Override
public TableCell call(TableColumn param) {
return new TableCell<Person, String>()
{
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if(isEmpty())
{
setText("");
}
else
{
setTextFill(Color.RED);
setFont(Font.font ("Verdana", 20));
setText(item);
}
}
};
}
});
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<>("lastName"));
lastNameCol.setCellFactory(new Callback<TableColumn, TableCell>() {
#Override
public TableCell call(TableColumn param)
{
return new TableCell<Person, String>()
{
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if(isEmpty())
{
setText("");
}
else
{
setTextFill(Color.BLUE);
setFont(Font.font ("Verdana", 20));
setText(item);
}
}
};
}
});
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol);
final Button addButton = new Button("Add");
addButton.setOnAction((ActionEvent e) -> {
data.add(new Person("Z","X"));
});
hb.getChildren().addAll(addButton);
hb.setSpacing(3);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(table, hb);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private Person(String fName, String lName) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
}
}
Sedrick's answer works perfectly well, and is a reasonable approach. I would just suggest a slight modification that allows separation of the style from the controller code using an external CSS file. The basic structure is the same, but instead of hard-coding the style in the cell implementation, simply set a style class on the cell, and then apply rules to that style class with CSS.
So the cell factory would look like
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<>("firstName"));
// modified cell factory:
firstNameCol.setCellFactory(tc -> {
TableCell<Person, String> cell =new TableCell<Person, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? "" : item);
}
};
// maybe choose a more suitable name here...
cell.getStyleClass().add("first-name-col");
return cell ;
});
Now attach an external css file to the scene (or some parent of the table) with the following:
.first-name-col.table-cell {
-fx-font-family: verdana ;
-fx-font-size: 20pt ;
-fx-text-fill: red ;
}
As well as separating the styles into a CSS file, which can easily be modified and can be identified by a logical name describing the reason for the style being applied (i.e. not "first-name-col" as used here), this also makes it easier if you have many columns, perhaps with some styles being shared between them and some styles being unique. You can easily write a method that generates the table cell with one or more style classes attached, so pretty much all the code gets reused, and then different styles are applied to different classes in the CSS.
You can also use this technique if the style might vary among the cells in the same column: simply add and remove style classes (or set CSS PseudoClasses) in the updateItem(...) method.
So, e.g.:
TableColumn<Person, Integer> todayTicketsCol = new TableColumn<>("Today assigned tickets");
// ...
todayTicketsCol.setCellFactory(tc -> {
TableCell<Person, Integer> cell = new TableCell<Person, Integer>() {
private final PseudoClass critical = PseudoClass.getPseudoClass("critical");
#Override
protected void updateItem(Integer numTickets, boolean empty) {
super.updateItem(numTickets, empty);
if (empty) {
setText("");
pseudoClassStateChanged(critical, false);
} else {
setText(numTickets.toString());
pseudoClassStateChanged(critical, numTickets.intValue() >= 50);
}
}
};
cell.getStyleClass().add("numeric");
return cell ;
});
With CSS, for example, like
.numeric.table-cell {
-fx-alignment: center-right ;
}
.numeric.table-cell:critical {
-fx-text-fill: red ;
}

JavaFX: how to recognise current row in TableView?

People Table - TableView Example
I've built a TableView in JavaFX as shown in the image (People Table) consisting of a list of names. How can I have the first and last name printed to console on the corresponding row every time the 'details' button is clicked?
(For reference my final goal and original unanswered question is for the details button to open a template scene and to populate it with data for that specific person).
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.event.*;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.stage.*;
import javafx.util.Callback;
public class cutExample extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(final Stage stage) {
stage.setTitle("People");
// create a table.
final TableView<Person> table = new TableView<>(
FXCollections.observableArrayList(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams"),
new Person("Emma", "Jones"),
new Person("Michael", "Brown")
)
);
// define the table columns.
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
TableColumn<Person, Boolean> actionCol = new TableColumn<>("Action");
actionCol.setSortable(false);
// create a cell value factory with a details button for each row in the table.
actionCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
#Override public TableCell<Person, Boolean> call(TableColumn<Person, Boolean> personBooleanTableColumn) {
return new AddPersonCell(stage, table);
}
});
table.getColumns().setAll(firstNameCol, lastNameCol, actionCol);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
stage.setScene(new Scene(table));
stage.show();
}
/** A table cell containing a button for details on each person. */
private class AddPersonCell extends TableCell<Person, Boolean> {
// a button for a specific person's details
final Button details = new Button("Details");
// pads and centers the button in the cell.
final StackPane paddedButton = new StackPane();
/**
* AddPersonCell constructor
* #param stage the stage in which the table is placed.
* #param table the table to which a new person can be added.
*/
AddPersonCell(final Stage stage, final TableView table) {
paddedButton.setPadding(new Insets(3));
paddedButton.getChildren().add(details);
}
/** places an details button in the row only if the row is not empty. */
#Override protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(paddedButton);
} else {
setGraphic(null);
}
}
}
}
You can use the following snippet of code:
actionCol.setCellFactory(column -> new TableCell<Person, Void>() {
#Override
protected void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
Button details = new Button("details");
details.setOnAction(e ->{Person person= (Person)getTableRow().getItem(); System.out.println(person.getFirstName()+", "+person.getLastName());});
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(details);
} else {
setGraphic(null);
}
}
});
Edit: to avoid casting you can use this:
details.setOnAction(e ->{int index = getTableRow().getIndex();
Person person= getTableView().getItems().get(index);
System.out.println(person.getFirstName()+", "+person.getLastName());});
Person somePersone= table.getSelectionModel().getSelectedItem();

How to dynamically create scenes in JavaFX?

So I've built a TableView in JavaFX as shown in the image (People Table) consisting of a list of names. For the final program, the info could be imported from a database so we do not know the number of people beforehand.
My question is how do I dynamically create a scene for each person so when the detail button is clicked on, it would switch to a unique scene so I can subsequently add info on that specific person (telephone number, address etc)?
Many thanks. Code below for reference:
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.event.*;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.stage.*;
import javafx.util.Callback;
public class cutExample extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(final Stage stage) {
stage.setTitle("People");
// create a table.
final TableView<Person> table = new TableView<>(
FXCollections.observableArrayList(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams"),
new Person("Emma", "Jones"),
new Person("Michael", "Brown")
)
);
// define the table columns.
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
TableColumn<Person, Boolean> actionCol = new TableColumn<>("Action");
actionCol.setSortable(false);
// create a cell value factory with a details button for each row in the table.
actionCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
#Override public TableCell<Person, Boolean> call(TableColumn<Person, Boolean> personBooleanTableColumn) {
return new AddPersonCell(stage, table);
}
});
table.getColumns().setAll(firstNameCol, lastNameCol, actionCol);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
stage.setScene(new Scene(table));
stage.show();
}
/** A table cell containing a button for details on each person. */
private class AddPersonCell extends TableCell<Person, Boolean> {
// a button for a specific person's details
final Button details = new Button("Details");
// pads and centers the button in the cell.
final StackPane paddedButton = new StackPane();
/**
* AddPersonCell constructor
* #param stage the stage in which the table is placed.
* #param table the table to which a new person can be added.
*/
AddPersonCell(final Stage stage, final TableView table) {
paddedButton.setPadding(new Insets(3));
paddedButton.getChildren().add(details);
}
/** places an details button in the row only if the row is not empty. */
#Override protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(paddedButton);
} else {
setGraphic(null);
}
}
}
}

TableMenuButton dose not show name of columns which are saved as Label, JavaFx

I turned my column names as suggested here to labels so that I can get tooltip on their names:
for (Entry<String, String> ent : dc.getSortedAssignedOrg().entrySet()) {
TreeTableColumn<String, ArrayList<String>> col = new TreeTableColumn<>();
Label label = new Label(ent.getValue());
col.setGraphic(label);
col.setEditable(false);
col.setSortable(false);
label.setTooltip(new Tooltip(label.getText()));// tooltip for column
.
.
.
Now the problem is my TableMenuButton does not show the column names, and clicking on the plus sign on the right corner of treetableview opens a list in which there are only the checked signs, which I can remove or add. But the name itself is not shown. How can I fix this?
You could create your own table menu. You're better off with a custom menu anyway, since the in-built menu e. g. closes each time you click on a button. Unfortunately there is no getter for the context menu, so you'll have to find access to it either via reflection or a lookup.
I created a gist for a custom menu via the reflection and the lookup mechanism. Maybe it's of help for you.
The relevant part for you would be
CheckBox cb = new CheckBox(tableColumn.getText());
where you set the menu item to the text of your preferrence, i. e. the text of your labels.
Here's a lookup version for TreeTableView:
CustomTreeTableMenuDemo.java
import java.util.Arrays;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class CustomTreeTableMenuDemo extends Application {
List<Employee> employees = Arrays.<Employee> asList(new Employee(
"Ethan Williams", "ethan.williams#example.com"), new Employee(
"Emma Jones", "emma.jones#example.com"), new Employee(
"Michael Brown", "michael.brown#example.com"), new Employee(
"Anna Black", "anna.black#example.com"), new Employee(
"Rodger York", "roger.york#example.com"), new Employee(
"Susan Collins", "susan.collins#example.com"));
final TreeItem<Employee> root = new TreeItem<>(new Employee(
"Sales Department", ""));
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
stage.setTitle("Table Menu Demo");
stage.setWidth(500);
stage.setHeight(550);
root.setExpanded(true);
employees.stream().forEach((employee) -> {
root.getChildren().add(new TreeItem<>(employee));
});
stage.setTitle("Tree Table View Sample");
final Scene scene = new Scene(new Group(), 400, 400);
scene.setFill(Color.LIGHTGRAY);
Group sceneRoot = (Group) scene.getRoot();
TreeTableColumn<Employee, String> empColumn = new TreeTableColumn<>(
"Employee");
empColumn.setPrefWidth(150);
empColumn
.setCellValueFactory((
TreeTableColumn.CellDataFeatures<Employee, String> param) -> new ReadOnlyStringWrapper(
param.getValue().getValue().getName()));
TreeTableColumn<Employee, String> emailColumn = new TreeTableColumn<>(
"Email");
emailColumn.setPrefWidth(190);
emailColumn
.setCellValueFactory((
TreeTableColumn.CellDataFeatures<Employee, String> param) -> new ReadOnlyStringWrapper(
param.getValue().getValue().getEmail()));
TreeTableView<Employee> treeTableView = new TreeTableView<>(root);
treeTableView.getColumns().setAll(empColumn, emailColumn);
sceneRoot.getChildren().add(treeTableView);
stage.setScene(scene);
stage.show();
// enable table menu button and add a custom menu to it
TreeTableUtils.addCustomTreeTableMenu(treeTableView);
}
public class Employee {
private SimpleStringProperty name;
private SimpleStringProperty email;
public SimpleStringProperty nameProperty() {
if (name == null) {
name = new SimpleStringProperty(this, "name");
}
return name;
}
public SimpleStringProperty emailProperty() {
if (email == null) {
email = new SimpleStringProperty(this, "email");
}
return email;
}
private Employee(String name, String email) {
this.name = new SimpleStringProperty(name);
this.email = new SimpleStringProperty(email);
}
public String getName() {
return name.get();
}
public void setName(String fName) {
name.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
}
TreeTableUtils.java
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Side;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.input.MouseEvent;
import com.sun.javafx.scene.control.skin.TableHeaderRow;
import com.sun.javafx.scene.control.skin.TreeTableViewSkin;
public class TreeTableUtils {
/**
* Make table menu button visible and replace the context menu with a custom context menu via reflection.
* The preferred height is modified so that an empty header row remains visible. This is needed in case you remove all columns, so that the menu button won't disappear with the row header.
* IMPORTANT: Modification is only possible AFTER the table has been made visible, otherwise you'd get a NullPointerException
* #param treeTableView
*/
public static void addCustomTreeTableMenu( TreeTableView treeTableView) {
// enable table menu
treeTableView.setTableMenuButtonVisible(true);
// replace internal mouse listener with custom listener
setCustomContextMenu( treeTableView);
}
private static void setCustomContextMenu( TreeTableView treeTableView) {
TreeTableViewSkin<?> treeTableViewSkin = (TreeTableViewSkin<?>) treeTableView.getSkin();
// get all children of the skin
ObservableList<Node> children = treeTableViewSkin.getChildren();
// find the TableHeaderRow child
for (int i = 0; i < children.size(); i++) {
Node node = children.get(i);
if (node instanceof TableHeaderRow) {
TableHeaderRow tableHeaderRow = (TableHeaderRow) node;
// setting the preferred height for the table header row
// if the preferred height isn't set, then the table header would disappear if there are no visible columns
// and with it the table menu button
// by setting the preferred height the header will always be visible
// note: this may need adjustments in case you have different heights in columns (eg when you use grouping)
double defaultHeight = tableHeaderRow.getHeight();
tableHeaderRow.setPrefHeight(defaultHeight);
for( Node child: tableHeaderRow.getChildren()) {
// child identified as cornerRegion in TableHeaderRow.java
if( child.getStyleClass().contains( "show-hide-columns-button")) {
// get the context menu
ContextMenu columnPopupMenu = createContextMenu( treeTableView);
// replace mouse listener
child.setOnMousePressed(me -> {
// show a popupMenu which lists all columns
columnPopupMenu.show(child, Side.BOTTOM, 0, 0);
me.consume();
});
}
}
}
}
}
/**
* Create a menu with custom items. The important thing is that the menu remains open while you click on the menu items.
* #param cm
* #param treeTableView
*/
private static ContextMenu createContextMenu( TreeTableView treeTableView) {
ContextMenu cm = new ContextMenu();
// create new context menu
CustomMenuItem cmi;
// select all item
Label showAll = new Label("Show all");
showAll.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
for (Object obj : treeTableView.getColumns()) {
((TableColumn<?, ?>) obj).setVisible(true);
}
}
});
cmi = new CustomMenuItem(showAll);
cmi.setHideOnClick(false);
cm.getItems().add(cmi);
// deselect all item
Label hideAll = new Label("Hide all");
hideAll.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
for (Object obj : treeTableView.getColumns()) {
((TableColumn<?, ?>) obj).setVisible(false);
}
}
});
cmi = new CustomMenuItem(hideAll);
cmi.setHideOnClick(false);
cm.getItems().add(cmi);
// separator
cm.getItems().add(new SeparatorMenuItem());
// menu item for each of the available columns
for (Object obj : treeTableView.getColumns()) {
TreeTableColumn<?, ?> tableColumn = (TreeTableColumn<?, ?>) obj;
CheckBox cb = new CheckBox(tableColumn.getText());
cb.selectedProperty().bindBidirectional(tableColumn.visibleProperty());
cmi = new CustomMenuItem(cb);
cmi.setHideOnClick(false);
cm.getItems().add(cmi);
}
return cm;
}
}

Categories