How to add button in JavaFX table view
Solution 1
To be able to render the column, TableColumn
needs cellValueFactory. But the "action" column does not exist in underlying data model. In this case, I just give some dummy value to cellValueFactory and move on:
public class JustDoIt extends Application {
private final TableView<Person> table = new TableView<>();
private final ObservableList<Person> data
= FXCollections.observableArrayList(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams"),
new Person("Emma", "Jones"),
new Person("Michael", "Brown")
);
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
stage.setWidth(450);
stage.setHeight(500);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setCellValueFactory(new PropertyValueFactory<>("firstName"));
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setCellValueFactory(new PropertyValueFactory<>("lastName"));
TableColumn actionCol = new TableColumn("Action");
actionCol.setCellValueFactory(new PropertyValueFactory<>("DUMMY"));
Callback<TableColumn<Person, String>, TableCell<Person, String>> cellFactory
= //
new Callback<TableColumn<Person, String>, TableCell<Person, String>>() {
@Override
public TableCell call(final TableColumn<Person, String> param) {
final TableCell<Person, String> cell = new TableCell<Person, String>() {
final Button btn = new Button("Just Do It");
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
btn.setOnAction(event -> {
Person person = getTableView().getItems().get(getIndex());
System.out.println(person.getFirstName()
+ " " + person.getLastName());
});
setGraphic(btn);
setText(null);
}
}
};
return cell;
}
};
actionCol.setCellFactory(cellFactory);
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, actionCol);
Scene scene = new Scene(new Group());
((Group) scene.getRoot()).getChildren().addAll(table);
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);
}
}
}
Solution 2
Here is my example using awesome Java 8 Functionality and extending TableCell class.
Let me give a quick explanation of what I am doing: I created a ActionButtonTableCell class that extends TableCell. And then you can use java 8 lamda functions to create an Action for the button.
import java.util.function.Function;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.util.Callback;
public class ActionButtonTableCell<S> extends TableCell<S, Button> {
private final Button actionButton;
public ActionButtonTableCell(String label, Function< S, S> function) {
this.getStyleClass().add("action-button-table-cell");
this.actionButton = new Button(label);
this.actionButton.setOnAction((ActionEvent e) -> {
function.apply(getCurrentItem());
});
this.actionButton.setMaxWidth(Double.MAX_VALUE);
}
public S getCurrentItem() {
return (S) getTableView().getItems().get(getIndex());
}
public static <S> Callback<TableColumn<S, Button>, TableCell<S, Button>> forTableColumn(String label, Function< S, S> function) {
return param -> new ActionButtonTableCell<>(label, function);
}
@Override
public void updateItem(Button item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(actionButton);
}
}
}
The implementation is then as simple as this, This is a sample button to remove the item from the table:
column.setCellFactory(ActionButtonTableCell.<Person>forTableColumn("Remove", (Person p) -> {
table.getItems().remove(p);
return p;
}));
![botenvouwer](https://i.stack.imgur.com/owYLC.jpg?s=256&g=1)
Comments
-
botenvouwer almost 4 years
I have searched at Google and Stackoverflow for this and I just don't get the given examples. Can someone please explain it to me.
I want to add a button to the last column of a table view and when it gets clicked it should trigger a listener and pass the object of the buttons row. I just do not get the following example from gist.github.com:
This is my full current code:
public class SchermdeelWerkplaats extends BorderPane{ //ATD moeder klasse met alle collecties etc. private ATD $; TableView tabel = new TableView(); Button nieuwTaak = new Button("Nieuwe taak inboeken"); final ObservableList<Task> data = FXCollections.observableArrayList(); public SchermdeelWerkplaats(ATD a) { $ = a; data.addAll($.agenda); tabel.setEditable(false); tabel.setPlaceholder(new Label("Geen taken")); TableColumn c1 = new TableColumn("datum"); c1.setMinWidth(200); TableColumn c2 = new TableColumn("type"); c2.setMinWidth(100); TableColumn c3 = new TableColumn("uren"); c3.setMinWidth(100); TableColumn c4 = new TableColumn("klaar"); c4.setMinWidth(200); TableColumn c5 = new TableColumn("Werknemer"); c5.setMinWidth(100); TableColumn c6= new TableColumn("Auto"); c6.setMinWidth(400); TableColumn c7= new TableColumn("Actie"); c7.setMinWidth(400); TableColumn col_action = new TableColumn<>("Action"); col_action.setCellValueFactory( new Callback<TableColumn.CellDataFeatures<Task, Boolean>, ObservableValue<Boolean>>() { @Override public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Task, Boolean> p) { return new SimpleBooleanProperty(p.getValue() != null); } }); col_action.setCellFactory( new Callback<TableColumn<Task, Task>, TableCell<Task, Task>>() { @Override public TableCell<Task, Task> call(TableColumn<Task, Task> p) { return new ButtonCell(); } } ); c1.setCellValueFactory( new PropertyValueFactory<Task,Date>("date") ); c2.setCellValueFactory( new PropertyValueFactory<Task,Task.TaskType>("type") ); c3.setCellValueFactory( new PropertyValueFactory<Task,Double>("hours") ); c4.setCellValueFactory( new PropertyValueFactory<Task,Boolean>("done") ); c5.setCellValueFactory( new PropertyValueFactory<Task,Employee>("employee") ); c6.setCellValueFactory( new PropertyValueFactory<Task,Car>("car") ); tabel.getColumns().addAll(c1, c2, c3, c4, c5, c6, c7); tabel.setItems(data); setCenter(tabel); setBottom(nieuwTaak); } //letterlijk van internet geplukt en datatype aangepast private class ButtonCell extends TableCell<Task, Task> { private Button cellButton; ButtonCell(){ cellButton = new Button("jjhjhjh"); cellButton.setOnAction(new EventHandler<ActionEvent>(){ @Override public void handle(ActionEvent t) { // do something when button clicked Task record = getItem(); // do something with record.... } }); } //Display button if the row is not empty @Override protected void updateItem(Task record, boolean empty) { super.updateItem(record, empty); if(!empty){ cellButton.setText("Something with "+record); setGraphic(cellButton); } else { setGraphic(null); } } } }
Now the part where I have to create a
ButtonCell extends TableCell
is understandable. But how to assign this to the column?I understand this:
c1.setCellValueFactory( new PropertyValueFactory<Task,Date>("date") );
But not this:
TableColumn col_action = new TableColumn<>("Action"); col_action.setCellValueFactory( new Callback<TableColumn.CellDataFeatures<Task, Boolean>, ObservableValue<Boolean>>() { @Override public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Task, Boolean> p) { return new SimpleBooleanProperty(p.getValue() != null); } }); col_action.setCellFactory( new Callback<TableColumn<Task, Task>, TableCell<Task, Task>>() { @Override public TableCell<Task, Task> call(TableColumn<Task, Task> p) { return new ButtonCell(); } } );