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?
Related
I want to reproduce that combobox in JavaFX
With swing there is a way by using a renderer where HTML code can be passed.
I haven't found a way yet to do it in JAVAFX. I saw the cellFactory but it seems to handle only one line of text.
I tried to use an observableList of WebView. I was able to display the information as desired, but then the onAction was never called. I tried by setting an event and a listener, but nothing worked. I tried the next examples separately.
cbOrderLine.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<WebView>() {
#Override
public void changed(ObservableValue<? extends WebView> arg0, WebView arg1, WebView arg2) {
int uasbdviadn = 0;
if (arg2 != null) {
System.out.println("Selected employee: " + arg2.getId() + " " + uasbdviadn);
}
}
});
cbOrderLine.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent ev) {
int index = cbOrderLine.getSelectionModel().getSelectedIndex();
initComboBox(index);
}
});
this is the code to create my dummy values
private List<WebView> createListFakeOrderLineItem() {
List<WebView> list = new ArrayList<>();
list.add(createFakeOrderLineItem(1));
list.add(createFakeOrderLineItem(2));
list.add(createFakeOrderLineItem(3));
list.add(createFakeOrderLineItem(4));
return list;
}
private WebView createFakeOrderLineItem(int id) {
WebView wv = new WebView();
wv.setPrefSize(684, 90);
ProductionOrderHeader header = new ProductionOrderHeader();
header.setPoNumber("PONumber1234");
ProductionOrderLine line = new ProductionOrderLine();
line.setId(3333);
ManufactureProduct product = new ManufactureProduct();
product.setId(1111);
product.setPartNumber("Product.PartNumber1111");
ManufacturePart part = new ManufacturePart();
part.setId(9999);
part.setPartNumber("Part.Partnumber9999");
part.setHardwareId(666666);
part.setHardwareName("Hardware Name");
OrderLineItem item = new OrderLineItem(id, header, line, part, product);
// return item;
wv.getEngine().loadContent(item.toString());
return wv;
}
This is my OrderLineItem
public class OrderLineItem {
Integer index;
ProductionOrderHeader header;
ProductionOrderLine line;
ManufacturePart part;
ManufactureProduct product;
public OrderLineItem(Integer index, ProductionOrderHeader header, ProductionOrderLine line, ManufacturePart part,
ManufactureProduct product) {
this.index = index;
this.header = header;
this.line = line;
this.part = part;
this.product = product;
}
public Integer getIndex() {
return index;
}
public void setIndex(Integer index) {
this.index = index;
}
public ProductionOrderHeader getHeader() {
return header;
}
public void setHeader(ProductionOrderHeader order) {
this.header = order;
}
public ProductionOrderLine getLine() {
return line;
}
public void setLine(ProductionOrderLine orderLine) {
this.line = orderLine;
}
public ManufacturePart getPart() {
return part;
}
public void setPart(ManufacturePart part) {
this.part = part;
}
public ManufactureProduct getProduct() {
return product;
}
public void setProduct(ManufactureProduct product) {
this.product = product;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("<html>");
sb.append("<table>");
sb.append("<tr>");
sb.append("<td valign=\"middle\" style=\"font-size: 32px; padding-right:5px;\">");
sb.append((index + 1));
sb.append("</td>");
sb.append("<td style=\"border: 1px solid #919191; width:616px; font-size:12px; padding:10px;\">");
sb.append("<b>");
sb.append(header.getPoNumber());
sb.append(" [");
sb.append(line.getId());
sb.append("] </b> -");
sb.append("<b>P/N: </b> #");
sb.append(product.getId());
sb.append("<b> ");
sb.append(product.getPartNumber());
sb.append("</b><br/>");
sb.append("Part: # ");
sb.append(part.getId());
sb.append("<b> ");
sb.append(part.getPartNumber());
sb.append("</b> H/W: # ");
sb.append(part.getHardwareId());
sb.append("<b> ");
sb.append(part.getHardwareName());
sb.append("</b>");
sb.append("</td>");
sb.append("</tr>");
sb.append("</table>");
sb.append("</html>");
return sb.toString();
}
}
I'm using JAVAFX 8.
Thanks
I saw the cellFactory but it seems to handle only one line of text.
Firstly your understanding of cellFactory is wrong. And also setting a WebView as your comboBox item is really a horrible idea :). Instead you can build a simple layout and set it as graphic in your cell factory.
And by the way, never set Nodes as items to your controls (ComboBox, ListView..etc), you can just pass your model and build the appropriate layout in cellFactories.
Below is a quick demo to give you an idea of how to build your desired comboBox. Hope this can help you to have a better understanding.
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MultiLineComboBoxDemo extends Application {
#Override
public void start(Stage stage) throws Exception {
VBox root = new VBox();
root.setSpacing(15);
root.setPadding(new Insets(25));
root.setAlignment(Pos.TOP_LEFT);
Scene sc = new Scene(root, 600, 600);
stage.setScene(sc);
stage.show();
final ObservableList<Person> items = FXCollections.observableArrayList();
for (int i = 1; i < 4; i++) {
items.add(new Person(i,"Name " + i, i + 30, "email" + i + "#test.com"));
}
final ComboBox<Person> comboBox = new ComboBox<>();
comboBox.setItems(items);
comboBox.setCellFactory(param -> new ListCell<Person>() {
#Override
protected void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setGraphic(buildLayout(item));
} else {
setGraphic(null);
}
}
});
comboBox.setButtonCell(new ListCell<Person>(){
#Override
protected void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setGraphic(buildLayout(item));
} else {
setGraphic(null);
}
}
});
comboBox.getSelectionModel().selectedItemProperty().addListener((obs,oldVal,selectedPerson)->{
System.out.println("Name : "+selectedPerson.getName());
// Do what you want..
});
root.getChildren().add(comboBox);
}
private HBox buildLayout(Person person) {
VBox layout = new VBox();
HBox.setHgrow(layout,Priority.ALWAYS);
layout.setStyle("-fx-border-width:1px;-fx-border-color:#444444;");
layout.setSpacing(5);
layout.setPadding(new Insets(2));
HBox topRow = new HBox();
topRow.setSpacing(5);
topRow.getChildren().addAll(getLabel("Name :","bold"),getLabel(person.getName(),"normal"), getLabel("Age :","bold"),getLabel(person.getAge()+"","normal"));
HBox bottomRow = new HBox();
bottomRow.setSpacing(5);
bottomRow.getChildren().addAll(getLabel("Email :","bold"),getLabel(person.getEmail(),"normal"));
layout.getChildren().addAll(topRow, bottomRow);
HBox pane = new HBox();
pane.setAlignment(Pos.CENTER_LEFT);
pane.setSpacing(5);
pane.setPadding(new Insets(2));
Label num = new Label(person.getId()+"");
num.setStyle("-fx-font-size:20px;-fx-font-weight:bold;-fx-text-fill:black;");
pane.getChildren().addAll(num,layout);
return pane;
}
private Label getLabel(String txt, String style){
Label lblName = new Label(txt);
lblName.setStyle("-fx-font-weight:"+style+";-fx-text-fill:black;");
return lblName;
}
public static void main(String[] args) {
Application.launch(args);
}
class Person {
IntegerProperty id = new SimpleIntegerProperty();
StringProperty name = new SimpleStringProperty();
IntegerProperty age = new SimpleIntegerProperty();
StringProperty email = new SimpleStringProperty();
public Person(int id,String name, int age, String email) {
setId(id);
setName(name);
setAge(age);
setEmail(email);
}
public int getId() {
return id.get();
}
public IntegerProperty idProperty() {
return id;
}
public void setId(int id) {
this.id.set(id);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public int getAge() {
return age.get();
}
public IntegerProperty ageProperty() {
return age;
}
public void setAge(int age) {
this.age.set(age);
}
public String getEmail() {
return email.get();
}
public StringProperty emailProperty() {
return email;
}
public void setEmail(String email) {
this.email.set(email);
}
}
}
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;
}));
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();
In my GUI i am having a TABLEVIEW and a HTMLEditor.
TableView having two columns. 1st column without edit feature. 2nd column have edit option.
for HTMLEditor i have written setOnKeyPressed property. In that property, in handle method i am trying to update 2nd column data. But data is not reflecting at all in TableView Control . But when i try to print data for 2nd column it is showing HTMLEditor's data only. But the HTMLEditor's data is not reflecting in TableView 2nd Column.
Following is my code
public class BinderDocumnetController implements Initializable {
public BinderDocumnetController() {
this.details = FXCollections.observableArrayList();
}
#Override
public void initialize(URL url, ResourceBundle rb) {
tblName.setCellValueFactory(new PropertyValueFactory<CData, String>("strName"));
tblRemarks.setCellValueFactory(new PropertyValueFactory<CData, String>("strRemarks"));
tblData.setItems(details);
tblData.setEditable(true);
Callback<TableColumn<CData, String>, TableCell<CData, String>> para4cellFactory
= new Callback<TableColumn<CData, String>, TableCell<CData, String>>() {
public TableCell call(TableColumn p) {
return new EditingCell();
}
};
tblRemarks.setCellFactory(para4cellFactory);
htmlEditor.setOnKeyReleased(new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent t) {
textField.setText(htmlEditor.getHtmlText());
System.out.println("row no. is "+tblData.getSelectionModel().getSelectedIndex());
tblData.getItems().get(tblData.getSelectionModel().getSelectedIndex()).setStrRemarks(htmlEditor.getHtmlText());
System.out.println(" data at row no. "+tblData.getSelectionModel().getSelectedIndex()+"is");
System.out.println(" "+tblData.getItems().get(tblData.getSelectionModel().getSelectedIndex()).getStrRemarks());
}
}
);
textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
htmlEditor.setHtmlText(textField.getText());
}
});
}
public void onbtnAddClicked(){
System.out.println(" in add");
CData cdata= new CData("XYZ","done");
details.add(cdata);
}
#FXML
HTMLEditor htmlEditor;
#FXML
TextField textField;
ObservableList<CData> details;
#FXML
TableView<CData> tblData;
#FXML
TableColumn<CData,String> tblRemarks,tblName;
#FXML
Button btnAdd;
class EditingCell extends TableCell<Object, String> {
private TextField textField;
public EditingCell() {
}
#Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> arg0,
Boolean arg1, Boolean arg2) {
if (!arg2) {
commitEdit(textField.getText());
}
}
});
textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
htmlEditor.setHtmlText(textField.getText());
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
}
following is CData.java code
public class CData {
SimpleStringProperty strName = new SimpleStringProperty("");
SimpleStringProperty strRemarks = new SimpleStringProperty("");
public CData(String strName,String strRemarks) {
setStrName(strName);
setStrRemarks(strRemarks);
}
public String getStrName() {
return strName.get();
}
public void setStrName(String strName) {
this.strName.set(strName);
}
public String getStrRemarks() {
return strRemarks.get();
}
public void setStrRemarks(String strRemarks) {
this.strRemarks.set(strRemarks);
}
public SimpleStringProperty strName() {
return strName;
}
public SimpleStringProperty strRemarks() {
System.out.println("\nRemarks: "+strRemarks.get()+"\n\n");
return strRemarks;
}
}
public SimpleStringProperty strNameProperty() {
return strName;
}
public SimpleStringProperty strRemarksProperty() {
System.out.println("\nRemarks: "+strRemarks.get()+"\n\n");
return strRemarks;
}
Done changes to above code now its working
I want to use a TreeView to display categories and actual content. I used this tutorial to follow my example, since they do something similiar but they simplify it.
I have something like this
public class Category {
final StringProperty categoryName = new SimpleStringProperty();
final ListProperty<Contact> contactList = new SimpleListProperty<>(FXCollections.<Contact>observableArrayList());
public Category(String name, List<Contact> contactList) {
this.categoryName.set(name);
this.contactList.setAll(contactList);
}
public StringProperty categoryNameProperty() { return this.categoryName; }
public ListProperty<Contact> contactListProperty() { return this.contactList; }
}
public class Contact {
final StringProperty contactName = new SimpleStringProperty();
public Contact(String name) {
this.contactName.set(name);
}
public StringProperty contactNameProperty() { return this.contactName; }
}
And now I want to build a TreeView out of a List<Category> with the underlying contacts automatically inserted as child nodes. Is this possible? If possible I would like not modify the Model itself. I thought of extending TreeItem<T> but I am not sure how far this will get me.
Ok I solved this with this, somebody by change a better solution?
public class Main extends Application {
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setWidth(300);
stage.setHeight(500);
final TreeView<MailTreeItem> tree = new TreeView<>();
final List<Category> categoryList = FXCollections.observableArrayList();
tree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TreeItem<MailTreeItem>>() {
#Override
public void changed(
ObservableValue<? extends TreeItem<MailTreeItem>> observable,
TreeItem<MailTreeItem> oldValue,
TreeItem<MailTreeItem> newValue) {
newValue.getValue();
}
});
final List<Contact> categoryContact1List = FXCollections.observableArrayList();
categoryContact1List.add(new Contact("Hans"));
categoryContact1List.add(new Contact("Dieter"));
final List<Contact> categoryContact2List = FXCollections.observableArrayList();
categoryContact2List.add(new Contact("Peter"));
categoryList.add(new Category("Freunde", categoryContact1List));
categoryList.add(new Category("Feinde", categoryContact2List));
final TreeItem<MailTreeItem> root = new TreeItem<MailTreeItem>(new RootTreeItem());
root.setExpanded(true);
tree.setRoot(root);
for (Category category : categoryList) {
final List<Contact> contactList = category.contactListProperty().get();
final TreeItem<MailTreeItem> categoryTreeItem = new TreeItem<MailTreeItem>(new CategoryTreeItem(category));
for (Contact contact : contactList) {
categoryTreeItem.getChildren().add(new TreeItem<MailTreeItem>(new ContactTreeItem(contact)));
}
root.getChildren().add(categoryTreeItem);
}
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(tree);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
public interface MailTreeItem {
public boolean isCategory();
public boolean isContact();
public Category getCategory();
public Contact getContact();
}
public class RootTreeItem implements MailTreeItem {
#Override public String toString() { return "root"; }
#Override public boolean isCategory() { return false; }
#Override public boolean isContact() { return false; }
#Override public Category getCategory() { return null; }
#Override public Contact getContact() { return null; }
}
public class CategoryTreeItem implements MailTreeItem {
private ObjectProperty<Category> category = new SimpleObjectProperty<>();
public CategoryTreeItem(Category category) {
this.category.set(category);
}
public ObjectProperty<Category> categoryProperty() { return this.category; }
#Override public String toString() { return this.category.get().categoryNameProperty().get(); }
public boolean isCategory() { return true; }
public boolean isContact() { return false; }
public Category getCategory() { return this.category.get(); }
public Contact getContact() { return null; }
}
public class ContactTreeItem implements MailTreeItem {
private final ObjectProperty<Contact> contact = new SimpleObjectProperty<>();
public ContactTreeItem(Contact contact) {
this.contact.set(contact);
}
public ObjectProperty<Contact> contactProperty() { return this.contact; }
#Override public String toString() { return this.contact.get().contactNameProperty().get(); }
public boolean isCategory() { return false; }
public boolean isContact() { return true; }
public Category getCategory() { return null; }
public Contact getContact() { return this.contact.get(); }
}
public class Category {
private final StringProperty categoryName = new SimpleStringProperty();
private final ListProperty<Contact> contactList = new SimpleListProperty<>(FXCollections.<Contact>observableArrayList());
public Category(String name, List<Contact> contactList) {
this.categoryName.set(name);
this.contactList.setAll(contactList);
}
public StringProperty categoryNameProperty() { return this.categoryName; }
public ListProperty<Contact> contactListProperty() { return this.contactList; }
}
public class Contact {
private final StringProperty contactName = new SimpleStringProperty();
public Contact(String name) {
this.contactName.set(name);
}
public StringProperty contactNameProperty() { return this.contactName; }
}
}