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();
}
}
);
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);
}
}
}
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;
}));
Related
I am working on a TableView (FXML) where I want to have all the rows accompanied with a delete button at the last column.
Here's a video that shows what I mean: YouTube Delete Button in TableView
Here's what I have in my main controller class:
public Button del() {
Button del = new Button();
del.setText("X");
del.setPrefWidth(30);
del.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
int i = index.get();
if(i > -1) {
goals.remove(i);
list.getSelectionModel().clearSelection();
}
}
});
return del;
}
private SimpleIntegerProperty index = new SimpleIntegerProperty();
#Override
public void initialize(URL location, ResourceBundle resources){
//DateFormat df = new SimpleDateFormat("dd MMM yyyy");
sdate.setValue(LocalDate.now());
edate.setValue(LocalDate.now());
seq.setCellValueFactory(new PropertyValueFactory<Goals, Integer>("id"));
gol.setCellValueFactory(new PropertyValueFactory<Goals, String>("goal"));
sdt.setCellValueFactory(new PropertyValueFactory<Goals, Date>("sdte"));
edt.setCellValueFactory(new PropertyValueFactory<Goals, Date>("edte"));
prog.setCellValueFactory(new PropertyValueFactory<Goals, Integer>("pb"));
del.setCellValueFactory(new PropertyValueFactory<Goals, Button>("x"));
list.setItems(goals);
list.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Object>() {
#Override
public void changed(ObservableValue<?> observable,
Object oldValue, Object newValue) {
index.set(goals.indexOf(newValue));
System.out.println("Index is: "+goals.indexOf(newValue));
}
});
}
Each time I launch the application, I will try to click the delete button from random rows but it always delete the first row. I guess the addListener method I use for list is not properly implemented and indexOf(newValue) is always 0 at every initialisation.
However, it will work if I click a row first and then click the delete button. But this is not what I want. I want users to be able to delete any row if they press the delete button without selecting the row.
Appreciate your help guys!
You need a custom cell factory defined for the column containing the delete button.
TableColumn<Person, Person> unfriendCol = new TableColumn<>("Anti-social");
unfriendCol.setCellValueFactory(
param -> new ReadOnlyObjectWrapper<>(param.getValue())
);
unfriendCol.setCellFactory(param -> new TableCell<Person, Person>() {
private final Button deleteButton = new Button("Unfriend");
#Override
protected void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (person == null) {
setGraphic(null);
return;
}
setGraphic(deleteButton);
deleteButton.setOnAction(
event -> getTableView().getItems().remove(person)
);
}
});
Here is a sample app. It doesn't use FXML, but you could adapt it to work with FXML very easily. Just click on an "Unfriend" button in the "Anti-social" column to delete a friend. Do it a lot and you will soon run out of friends.
import javafx.application.Application;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class GestureEvents extends Application {
private 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) {
final Label label = new Label("Friends");
label.setFont(new Font("Arial", 20));
final Label actionTaken = new Label();
TableColumn<Person, Person> unfriendCol = new TableColumn<>("Anti-social");
unfriendCol.setMinWidth(40);
unfriendCol.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue()));
unfriendCol.setCellFactory(param -> new TableCell<Person, Person>() {
private final Button deleteButton = new Button("Unfriend");
#Override
protected void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (person == null) {
setGraphic(null);
return;
}
setGraphic(deleteButton);
deleteButton.setOnAction(event -> data.remove(person));
}
});
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<>("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<>("lastName"));
table.setItems(data);
table.getColumns().addAll(unfriendCol, firstNameCol, lastNameCol);
table.setPrefHeight(250);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 10, 10, 10));
vbox.getChildren().addAll(label, table, actionTaken);
VBox.setVgrow(table, Priority.ALWAYS);
stage.setScene(new Scene(vbox));
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);
}
}
}
I have the following code to create a TreeTableView with a CheckComboBox in one column.
public class TreeTableViewSample extends Application {
List<Employee> employees = Arrays.<Employee>asList(
new Employee("Ethan Williams", ""),
new Employee("Emma Jones", ""),
new Employee("Michael Brown", ""),
new Employee("Anna Black", ""),
new Employee("Rodger York", ""),
new Employee("Susan Collins", ""));
final TreeItem<Employee> root = new TreeItem<>(new Employee("Department", ""));
final TreeItem<Employee> root1 = new TreeItem<>(new Employee("Executive Department", ""));
final TreeItem<Employee> root2 = new TreeItem<>(new Employee("Sales Department", ""));
public static void main(String[] args) {
Application.launch(TreeTableViewSample.class, args);
}
#Override
public void start(Stage stage) {
root.setExpanded(true);
employees.stream().forEach((employee) -> {
root1.getChildren().add(new TreeItem<>(employee));
root2.getChildren().add(new TreeItem<>(employee));
});
root.getChildren().addAll(root1, root2);
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.setEditable(true);
emailColumn.setCellFactory((TreeTableColumn<Employee, String> p) -> new CheckComboCell());
emailColumn.setCellValueFactory(
(TreeTableColumn.CellDataFeatures<Employee, String> param)
-> new ReadOnlyStringWrapper(param.getValue().getValue().getEmail())
);
TreeTableView<Employee> treeTableView = new TreeTableView<>(root);
treeTableView.setEditable(true);
treeTableView.getColumns().setAll(empColumn, emailColumn);
sceneRoot.getChildren().add(treeTableView);
stage.setScene(scene);
stage.show();
}
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);
}
}
}
Cell Factory Class
public class CheckComboCell extends TreeTableCell<TreeTableViewSample.Employee, String> {
private final ComboBox<CheckComboCellModel> cb = new ComboBox<CheckComboCellModel>() {
#SuppressWarnings("restriction")
#Override
protected javafx.scene.control.Skin<?> createDefaultSkin() {
return new ComboBoxListViewSkin<CheckComboCellModel>(this) {
#Override
protected boolean isHideOnClickEnabled() {
return false;
}
};
}
};
private final ObservableList<CheckComboCellModel> items = FXCollections.observableArrayList();
private final Tooltip tooltip = new Tooltip();
public CheckComboCell() {
super();
populateAllocationType();
cb.setItems(items);
cb.setCellFactory((ListView<CheckComboCellModel> p) -> new ListCell<CheckComboCellModel>() {
private final CheckBox checkBox = new CheckBox();
private BooleanProperty booleanProperty;
{
checkBox.setOnAction(e -> getListView().getSelectionModel().select(getItem()));
}
#Override
protected void updateItem(CheckComboCellModel item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
checkBox.setText(item.toString());
if (booleanProperty != null) {
checkBox.selectedProperty().unbindBidirectional(booleanProperty);
}
booleanProperty = item.checkProperty();
checkBox.selectedProperty().bindBidirectional(booleanProperty);
setGraphic(checkBox);
} else {
setGraphic(null);
setText(null);
}
}
});
cb.setButtonCell(new ListCell<CheckComboCellModel>() {
#Override
protected void updateItem(CheckComboCellModel item, boolean empty) {
super.updateItem(item, empty);
String selected = cb.getItems().stream().filter(i -> i.getCheck())
.map(i -> i + "").collect(Collectors.joining(","));
setText(selected);
}
});
setGraphic(null);
}
private void populateAllocationType() {
items.add(new CheckComboCellModel("mail_1"));
items.add(new CheckComboCellModel("mail_2"));
items.add(new CheckComboCellModel("mail_3"));
items.add(new CheckComboCellModel("mail_4"));
items.add(new CheckComboCellModel("mail_5"));
}
#Override
public void startEdit() {
if (!isEditable() || !getTreeTableView().isEditable() || !getTableColumn().isEditable()) {
return;
}
super.startEdit();
setGraphic(cb);
}
#Override
public void commitEdit(String value) {
super.commitEdit(value);
setGraphic(null);
setText(cb.getButtonCell().getText());
}
#Override
public void cancelEdit() {
super.cancelEdit();
setGraphic(null);
setText(cb.getButtonCell().getText());
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setText(this.cb.getButtonCell().getText());
setGraphic(null);
}
}
}
Model class
public class CheckComboCellModel {
private final BooleanProperty check = new SimpleBooleanProperty(false);
private final StringProperty item = new SimpleStringProperty();
public CheckComboCellModel() {
}
public CheckComboCellModel(String item) {
this.item.set(item);
}
public CheckComboCellModel(String item, Boolean check) {
this.item.set(item);
this.check.set(check);
}
public BooleanProperty checkProperty() {
return check;
}
public Boolean getCheck() {
return check.getValue();
}
public void setCheck(Boolean value) {
check.set(value);
}
public StringProperty itemProperty() {
return item;
}
public String getItem() {
return item.getValueSafe();
}
public void setItem(String value) {
item.setValue(value);
}
#Override
public String toString() {
return item.getValue();
}
}
With this i get what i expect (i.e) a Column with CheckComboBox. But the issue is:
The application looks like this:
I selected some values
The values are updated in cell
But when the node is minimized
The updated value is displayed in next node. What i am doing wrong ? How can i solve this?
I am trying to wrap the text in the ComboBox but I cannot really manage it.
I need the wrapped text in the editor part, not in the dropdown part of the comboBox. I see that the editor is a TextField, and its text cannot really be wrapped or?
Is there any trick or solution to have a nicely wrapped text so if I have a really long text I can see it every time?
So instead of displaying the ... I want the whole text to be displayed.
Code part I don't know if I should add since it is really simple table cell, and a ComboBox set as Graphics, but if it helps I will edit the question.
Note: Setting the colum wider so the text can fit, is not a solution!
Here is a screenshot:
I am bit confused about the question. You want to show the wrapped text while editing or while in non edit mode.? Because if you are in editing mode you cannot see ... in text box.
So I assume you are asking for showing the wrapped text after selecting from popup list and the combo is not in edit mode.We can fix that using buttonCell.
If this is what you are not asking for, then a Minimal, Complete, and Verifiable example can help me to look into the actual issue.
Please refer to the below code of how to show the wrapped text.
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TableViewComboBoxCell extends Application {
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data = FXCollections
.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson",
"isabella.johnson#example.com"),
new Person("Ethan", "Williams",
"ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com"));
public static void main(String[] args) {
launch(args);
}
private final ObservableList<String> comboList = FXCollections.observableArrayList("First big sentence with very long text to check the text wrap",
"Second big sentence with very long text to check the text wrap",
"Third big sentence with very long text to check the text wrap",
"Fourth big sentence with very long text to check the text wrap");
#Override
public void start(Stage stage) {
for (int i = 0; i < 50; i++) {
if(i%5==0){
data.add(new Person("Name " + i, "Last " + i, "Mail " + i, comboList.get(0)));
}else {
data.add(new Person("Name " + i, "Last " + i, "Mail " + i));
}
}
Scene scene = new Scene(new StackPane());
stage.setTitle("Table View Sample");
stage.setWidth(650);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol
.setCellValueFactory(new PropertyValueFactory<Person, String>(
"firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol
.setCellValueFactory(new PropertyValueFactory<Person, String>(
"lastName"));
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>(
"email"));
TableColumn<Person, String> comboCol = new TableColumn<>("Combo");
comboCol.setMinWidth(200);
comboCol.setCellValueFactory(new PropertyValueFactory<Person, String>(
"combo"));
comboCol.setCellFactory(new Callback<TableColumn<Person, String>, TableCell<Person, String>>() {
#Override
public TableCell<Person, String> call(TableColumn<Person, String> param) {
return new TableCell<Person, String>() {
private ComboBox<String> combo;
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
getCombo().getSelectionModel().clearSelection();
if (!empty) {
getCombo().setValue(item);
setGraphic(getCombo());
} else {
setGraphic(null);
}
}
private ComboBox<String> getCombo() {
if (combo == null) {
combo = new ComboBox<>();
combo.setItems(comboList);
combo.getSelectionModel().selectedItemProperty().addListener((obs, old, newVal) -> {
((Person) getTableRow().getItem()).setCombo(newVal);
});
combo.setButtonCell(new ListCell<String>() {
private Text textLbl;
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setGraphic(null);
if (!empty) {
getTextLbl().setText(item);
setGraphic(getTextLbl());
}
}
private Text getTextLbl(){
if(textLbl ==null){
textLbl = new Text();
textLbl.wrappingWidthProperty().bind(this.widthProperty().subtract(10));
}
return textLbl;
}
});
}
return combo;
}
};
}
});
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, comboCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((StackPane) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private final SimpleStringProperty combo;
private Person(String fName, String lName, String email) {
this(fName,lName,email,null);
}
private Person(String fName, String lName, String email, String comboStr) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
this.combo = new SimpleStringProperty(comboStr);
}
public String getFirstName() {
return firstName.get();
}
public SimpleStringProperty firstNameProperty() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public String getLastName() {
return lastName.get();
}
public SimpleStringProperty lastNameProperty() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public String getEmail() {
return email.get();
}
public SimpleStringProperty emailProperty() {
return email;
}
public void setEmail(String email) {
this.email.set(email);
}
public String getCombo() {
return combo.get();
}
public SimpleStringProperty comboProperty() {
return combo;
}
public void setCombo(String combo) {
this.combo.set(combo);
}
}
}
And the output is as below:
I have an event listener on a TableView which listens for mouse events. How can I get the mouse clicked cell index (and change focus to the new cell) when a mouse event is thrown.
public class PrnTableController
{
#FXML
private TableView<SimpleStringProperty> table;
#FXML
private TableColumn<SimpleStringProperty, String> data;
#FXML
private void initialize()
{
this.data.setCellValueFactory(cellData -> cellData.getValue());
this.data.setCellFactory(event -> new EditCell(this.observablePrnPropertyData, this.table));
// Add mouse Listener
this.table.setOnMouseClicked(event -> this.handleOnMouseClick(event));
}
private void handleOnMouseClick(MouseEvent event)
{
TableView tv = (TableView) event.getSource();
// TODO : get the mouse clicked cell index
int index = ???
if (event.getButton().equals(MouseButton.PRIMARY))
{
if (event.getClickCount() == 2)
{
LOGGER.info("Double clicked on cell");
final int focusedIndex = this.table.getSelectionModel().getFocusedIndex();
if (index == focusedIndex)
{
// TODO : Double click
}
}
else if (event.getClickCount() == 1)
{
// TODO : Single click
}
}
}
}
I have managed to get the clicked cell index when the mouse event is on the Cell but not the table.
The following code can be used to get the clicked cell index when the event is on the Cell. I've had problems with selecting and changing focus when the mouse event is on TabelCell. The focus does not change to the new cell. It changes if you double click. With a single click nothing happens. I suspect thats because I have other event listeners, there may be conflicting events. I have the following event on the TableCell - setOnDragDetected, setOnMouseDragEntered and the following event on the TableView - addEventFilter, setOnKeyPressed, setOnEditCommit.
TableCell<Map<String, SimpleStringProperty>, String> cell = (TableCell<Map<String, SimpleStringProperty>, String>) mouseEvent.getSource();
int index = cell.getIndex();
Here is an example with the problem. Basically when you click on an cell, you can see that the event is registered but nothing happens. I mean the focus does change to the newly clicked cell.
public class TableViewEditOnType extends Application
{
private TableView<Person> table;
private ObservableList<Person> observableListOfPerson;
#Override
public void start(Stage primaryStage)
{
this.table = new TableView<>();
this.table.getSelectionModel().setCellSelectionEnabled(true);
this.table.setEditable(true);
TableColumn<Person, String> firstName = this.createColumn("First Name", Person::firstNameProperty);
TableColumn<Person, String> lastName = this.createColumn("Last Name", Person::lastNameProperty);
TableColumn<Person, String> email = this.createColumn("Email", Person::emailProperty);
this.table.getColumns().add(firstName);
this.table.getColumns().add(lastName);
this.table.getColumns().add(email);
this.observableListOfPerson = FXCollections.observableArrayList();
this.observableListOfPerson.add(new Person("Jacob", "Smith", "jacob.smith#example.com"));
this.observableListOfPerson.add(new Person("Isabella", "Johnson", "isabella.johnson#example.com"));
this.observableListOfPerson.add(new Person("Ethan", "Williams", "ethan.williams#example.com"));
this.observableListOfPerson.add(new Person("Emma", "Jones", "emma.jones#example.com"));
this.observableListOfPerson.add(new Person("Michael", "Brown", "michael.brown#example.com"));
this.table.getItems().addAll(this.observableListOfPerson);
firstName.setOnEditCommit(event -> this.editCommit(event, "firstName"));
lastName.setOnEditCommit(event -> this.editCommit(event, "lastName"));
email.setOnEditCommit(event -> this.editCommit(event, "email"));
this.table.setOnKeyPressed(event -> {
TablePosition<Person, ?> pos = this.table.getFocusModel().getFocusedCell();
if (pos != null)
{
this.table.edit(pos.getRow(), pos.getTableColumn());
}
});
Scene scene = new Scene(new BorderPane(this.table), 880, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private void editCommit(CellEditEvent<Person, String> event, String whatEdited)
{
if (whatEdited.equals("firstName"))
{
event.getTableView().getItems().get(event.getTablePosition().getRow()).setFirstName(event.getNewValue());
}
else if (whatEdited.equals("lastName"))
{
event.getTableView().getItems().get(event.getTablePosition().getRow()).setLastName(event.getNewValue());
}
else if (whatEdited.equals("email"))
{
event.getTableView().getItems().get(event.getTablePosition().getRow()).setEmail(event.getNewValue());
}
}
private TableColumn<Person, String> createColumn(String title, Function<Person, StringProperty> property)
{
TableColumn<Person, String> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
col.setCellFactory(column -> new EditCell(property, this.table, this.observableListOfPerson));
return col;
}
private static class EditCell extends TableCell<Person, String>
{
private final TextField textField = new TextField();
private final Function<Person, StringProperty> property;
private TableView table;
private ObservableList<Person> observableListOfPerson;
EditCell(Function<Person, StringProperty> property, TableView table, ObservableList<Person> observableListOfPerson)
{
this.property = property;
this.table = table;
this.observableListOfPerson = observableListOfPerson;
this.textProperty().bind(this.itemProperty());
this.setGraphic(this.textField);
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
this.textField.setOnAction(evt -> {
this.commitEdit(this.textField.getText());
});
this.textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (!isNowFocused)
{
this.commitEdit(this.textField.getText());
}
});
// On mouse click event
this.setOnMouseClicked(mouseEvent -> this.handleCellMouseClick(mouseEvent));
}
private void handleCellMouseClick(final MouseEvent mouseEvent)
{
System.out.println("MOUSE EVENT");
TableCell<Map<String, SimpleStringProperty>, String> cell = (TableCell<Map<String, SimpleStringProperty>, String>) mouseEvent.getSource();
int index = cell.getIndex();
// Set up the map data structure before editing
this.validCell(index);
if (mouseEvent.getButton().equals(MouseButton.PRIMARY))
{
if (mouseEvent.getClickCount() == 2)
{
System.out.println("Double clicked on cell");
final int focusedIndex = this.table.getSelectionModel().getFocusedIndex();
if (index == focusedIndex)
{
this.changeTableCellFocus(this.table, index);
}
}
else if (mouseEvent.getClickCount() == 1)
{
System.out.println("Single click on cell");
this.changeTableCellFocus(this.table, index);
}
}
}
private void validCell(final int cellIndex)
{
if (cellIndex >= this.observableListOfPerson.size())
{
for (int x = this.observableListOfPerson.size(); x <= cellIndex; x++)
{
this.observableListOfPerson.add(new Person("", "", ""));
}
}
}
public void changeTableCellFocus(final TableView<?> table, final int focusIndex)
{
table.requestFocus();
table.getSelectionModel().clearAndSelect(focusIndex);
table.getFocusModel().focus(focusIndex);
}
#Override
public void startEdit()
{
super.startEdit();
this.textField.setText(this.getItem());
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
this.textField.requestFocus();
}
#Override
public void cancelEdit()
{
super.cancelEdit();
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
#Override
public void commitEdit(String text)
{
super.commitEdit(text);
Person person = this.getTableView().getItems().get(this.getIndex());
StringProperty cellProperty = this.property.apply(person);
cellProperty.set(text);
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
public static class Person
{
private final StringProperty firstName = new SimpleStringProperty();
private final StringProperty lastName = new SimpleStringProperty();
private final StringProperty email = new SimpleStringProperty();
public Person(String firstName, String lastName, String email)
{
this.setFirstName(firstName);
this.setLastName(lastName);
this.setEmail(email);
}
public final StringProperty firstNameProperty()
{
return this.firstName;
}
public final java.lang.String getFirstName()
{
return this.firstNameProperty().get();
}
public final void setFirstName(final java.lang.String firstName)
{
this.firstNameProperty().set(firstName);
}
public final StringProperty lastNameProperty()
{
return this.lastName;
}
public final java.lang.String getLastName()
{
return this.lastNameProperty().get();
}
public final void setLastName(final java.lang.String lastName)
{
this.lastNameProperty().set(lastName);
}
public final StringProperty emailProperty()
{
return this.email;
}
public final java.lang.String getEmail()
{
return this.emailProperty().get();
}
public final void setEmail(final java.lang.String email)
{
this.emailProperty().set(email);
}
}
public static void main(String[] args)
{
launch(args);
}
}
Try this :
TableCell tc = (TableCell) event.getSource();
int index = tc.getIndex();
I'm attempting to make TableView behave like Excel. I am having trouble selected the correct cells.
In Excel, if you select a column(say 5 adjacent cells in the column) and then you click SHIFT + LEFT, excel will automatically select all corresponding left most cell.
e.g.
Say in Excel I have [A1][A2][A3] selects and then I click SHIFT + RIGHT, know the selected cells would be:
[A1][B1][A2][B2][A3][B3]
How can I achieve similar behavior with a TableView.
I've tried doing something like the following with no luck.
// Table view
TableView tv = new TableView();
// Some more code goes here
// Select right most element
tv.getSelectionModel().selectRightCell();
The problem with selectRightCell() or selectLeftCell() function is that it selects just a single cell (to the left or right of the selected cell) not the all left or right cell of the selected cells.
Here is sample code below, you can select by dragging by mouse or by pressing control+arrow keys.
import com.sun.javafx.scene.control.behavior.TableCellBehavior;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseDragEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;
import java.util.Arrays;
public class DragSelectionTable extends Application {
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com")
);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(450);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
final Callback<TableColumn<Person, String>, TableCell<Person, String>> cellFactory = new DragSelectionCellFactory();
firstNameCol.setCellFactory(cellFactory);
lastNameCol.setCellFactory(cellFactory);
emailCol.setCellFactory(cellFactory);
table.setEditable(true);
table.setItems(data);
table.getColumns().addAll(Arrays.asList(firstNameCol, lastNameCol, emailCol));
table.getSelectionModel().setCellSelectionEnabled(true);
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
table.addEventHandler(KeyEvent.KEY_RELEASED, new ControlDownSelectionEventHandler());
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public class DragSelectionCellFactory implements Callback<TableColumn<Person, String>, TableCell<Person, String>> {
#Override
public TableCell<Person, String> call(final TableColumn<Person, String> col) {
return new DragSelectionCell();
}
}
public class DragSelectionCell extends TableCell<Person, String> {
public DragSelectionCell() {
setOnDragDetected(new DragDetectedEventHandler(this));
setOnMouseDragEntered(new DragEnteredEventHandler(this));
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
setText(item);
}
}
}
public class DragDetectedEventHandler implements EventHandler<MouseEvent> {
TableCell<Person, String> tableCell;
public DragDetectedEventHandler(TableCell<Person, String> tableCell) {
this.tableCell = tableCell;
}
#Override
public void handle(final MouseEvent event) {
tableCell.startFullDrag();
}
}
public class DragEnteredEventHandler implements EventHandler<MouseDragEvent> {
TableCell<Person, String> tableCell;
public DragEnteredEventHandler(TableCell<Person, String> tableCell) {
this.tableCell = tableCell;
}
#Override
public void handle(final MouseDragEvent event) {
performSelection(tableCell.getTableView(), tableCell.getTableColumn(), tableCell.getIndex());
}
}
public class ControlDownSelectionEventHandler implements EventHandler<KeyEvent> {
#Override
public void handle(final KeyEvent event) {
KeyCode code = event.getCode();
if (event.isShiftDown() && (KeyCode.UP.equals(code) || KeyCode.DOWN.equals(code) || KeyCode.LEFT.equals(code) || KeyCode.RIGHT.equals(code))) {
int index = table.getFocusModel().getFocusedCell().getRow();
TableColumn column = table.getFocusModel().getFocusedCell().getTableColumn();
performSelection(table, column, index);
}
}
}
protected void performSelection(TableView<Person> table, TableColumn<Person, String> column, int index) {
final TablePositionBase anchor = TableCellBehavior.getAnchor(table, table.getFocusModel().getFocusedCell());
int columnIndex = table.getVisibleLeafIndex(column);
int minRowIndex = Math.min(anchor.getRow(), index);
int maxRowIndex = Math.max(anchor.getRow(), index);
TableColumnBase minColumn = anchor.getColumn() < columnIndex ? anchor.getTableColumn() : column;
TableColumnBase maxColumn = anchor.getColumn() >= columnIndex ? anchor.getTableColumn() : column;
table.getSelectionModel().clearSelection();
final int minColumnIndex = table.getVisibleLeafIndex((TableColumn) minColumn);
final int maxColumnIndex = table.getVisibleLeafIndex((TableColumn) maxColumn);
for (int _row = minRowIndex; _row <= maxRowIndex; _row++) {
for (int _col = minColumnIndex; _col <= maxColumnIndex; _col++) {
table.getSelectionModel().select(_row, table.getVisibleLeafColumn(_col));
}
}
table.getFocusModel().focus(index, column);
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
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);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
}