right click event and double click event on tableview javafx - java

I am rewriting an application from swing to javafx.
I do not understand how to implement a double click event and a right click event on the same row of a tableview.
Separately they work ok.
Thi is my code for right click behaviour.
words_table.setRowFactory(
new Callback<TableView<WordsToFind>, TableRow<WordsToFind>>() {
#Override
public TableRow<WordsToFind> call(TableView<WordsToFind> tableView) {
final TableRow<WordsToFind> row = new TableRow<>();
final ContextMenu rowMenu = new ContextMenu();
MenuItem removeItem = new MenuItem("Delete");
removeItem.setOnAction(e -> {
int wordid = words_table.getSelectionModel().getSelectedItem().getWordToFindId();
deleteWord(wordid);
words_table.getItems().remove(row.getItem());
});
rowMenu.getItems().addAll(removeItem);
row.contextMenuProperty().bind(
Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu)null));
return row;
}
});
This is my code for double click behaviour
words_table.setRowFactory(
new Callback<TableView<WordsToFind>, TableRow<WordsToFind>>() {
#Override
public TableRow<WordsToFind> call(TableView<WordsToFind> tableView) {
final TableRow<WordsToFind> row = new TableRow<>();
row.setOnMouseClicked(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event){
if (event.getClickCount() == 2 && (! row.isEmpty()) ) {
some code here .....
}
}
});
return row;
}
});
Thanks Alb

Just put the row.setOnMouseClicked call in the call() method of the first row factory.
words_table.setRowFactory(tableView -> {
final TableRow<WordsToFind> row = new TableRow<>();
final ContextMenu rowMenu = new ContextMenu();
MenuItem removeItem = new MenuItem("Delete");
removeItem.setOnAction(e -> {
int wordid = words_table.getSelectionModel().getSelectedItem().getWordToFindId();
deleteWord(wordid);
words_table.getItems().remove(row.getItem());
});
rowMenu.getItems().addAll(removeItem);
row.contextMenuProperty().bind(
Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu)null));
row.setOnMouseClicked(event -> {
if (event.getClickCount() == 2 && (! row.isEmpty()) ) {
// some code here .....
}
});
return row;
});
(I converted the anonymous inners classes to lambda expressions for readability.)
Here is a complete example that demonstrates this working:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class RowFactoryExample extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
table.getColumns().add(column("Item", Item::nameProperty));
table.getColumns().add(column("Value", Item::valueProperty));
table.getItems().setAll(createData());
table.setRowFactory(tableView -> {
final TableRow<Item> row = new TableRow<>();
final ContextMenu rowMenu = new ContextMenu();
MenuItem removeItem = new MenuItem("Delete");
removeItem.setOnAction(e -> {
table.getItems().remove(row.getItem());
});
rowMenu.getItems().addAll(removeItem);
row.contextMenuProperty().bind(
Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu)null));
row.setOnMouseClicked(event -> {
if (event.getClickCount() == 2 && (! row.isEmpty()) ) {
System.out.println("Double click on "+row.getItem().getName());
}
});
return row;
});
Scene scene = new Scene(table, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private <S,T> TableColumn<S,T> column(String text, Function<S, ObservableValue<T>> prop) {
TableColumn<S,T> col = new TableColumn<>(text);
col.setCellValueFactory(cellData -> prop.apply(cellData.getValue()));
return col ;
}
private List<Item> createData() {
Random rng = new Random();
List<Item> data = new ArrayList<>();
for (int i = 1 ; i <= 100; i++) {
data.add(new Item("Item "+i, rng.nextInt(1000))) ;
}
return data ;
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty value = new SimpleIntegerProperty();
public Item(String name, int value) {
setName(name);
setValue(value);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
public final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
}
public static void main(String[] args) {
launch(args);
}
}

You can add both events to the row by doing row.setOnMouseClicked(..) itself as shown below
words_table.setRowFactory(
new Callback<TableView<WordsToFind>, TableRow<WordsToFind>>() {
#Override
public TableRow<WordsToFind> call(TableView<WordsToFind> tableView) {
final TableRow<WordsToFind> row = new TableRow<>();
row.setOnMouseClicked(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event){
if (event.getClickCount() == 2 && (! row.isEmpty()) ) {
//double click code here
}
else if(event.isSecondaryButtonDown()){
//right click code here
}
}
});
return row;
}
});

Related

How to put a ProcessIndicator in each row of a TableView and indicate task status

I'm trying to do the following in JavaFX:
Have a TableView with multiple rows.
Each row contains columns with text and one Progress/Status column.
When a specific Button is pressed, for each row of the TableView some task should be performed, one row after the other. (e.g. check some data, ...)
While this task is performed, a indeterminate ProgressIndicator shall be shown in the Status column, until the task for this row is finished, then the indicator shows as done.
When all tasks for each row are done, the button can be pressed again to reset the status and execute the tasks again.
I had found some help in this related Stackoverflow post and also here and tried to tweak this as needed but got stuck on some issues:
Currently, each ProgressIndicator for each row is displayed immediately (as indeterminate) when I run the program. How can I only activate them / make them visible for each row one after another once the button is pressed?
Pressing the button again once the fake tasks are done does not restart it. How would I have to modify / rebuild the program to make resets possible?
Does the overall approach make sense?
My current runnable code:
import java.util.Random;
import java.util.concurrent.*;
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public class ProgressIndicatorTableCellTest extends Application {
public void start(Stage primaryStage) {
TableView<TestTask> table = new TableView<>();
Random rng = new Random();
for (int i = 0; i < 3; i++) {
table.getItems().add(new TestTask(rng.nextInt(3000) + 2000, "Test"));
}
TableColumn<TestTask, String> nameCol = new TableColumn("Name");
nameCol.setCellValueFactory(new PropertyValueFactory<TestTask, String>("name"));
nameCol.setPrefWidth(75);
TableColumn<TestTask, Double> progressCol = new TableColumn("Progress");
progressCol.setCellValueFactory(new PropertyValueFactory<TestTask, Double>("progress"));
progressCol.setCellFactory(ProgressIndicatorTableCell.<TestTask>forTableColumn());
table.getColumns().addAll(nameCol, progressCol);
BorderPane root = new BorderPane();
root.setCenter(table);
Button btn = new Button("Start");
btn.setOnAction(actionEvent -> {
ExecutorService executor = Executors.newSingleThreadExecutor();
for (TestTask task : table.getItems()) {
executor.submit(task);
}
});
root.setBottom(btn);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public static class TestTask extends Task<Void> {
private final int waitTime; // milliseconds
final ReadOnlyStringWrapper name = new ReadOnlyStringWrapper();
public static final int NUM_ITERATIONS = 100;
public TestTask(int waitTime, String name) {
this.waitTime = waitTime;
this.name.set(name);
}
public ReadOnlyStringProperty nameProperty() {
return name.getReadOnlyProperty();
}
#Override
protected Void call() throws Exception {
this.updateProgress(ProgressIndicator.INDETERMINATE_PROGRESS, 1);
Thread.sleep(waitTime);
this.updateProgress(1, 1);
return null;
}
}
}
class ProgressIndicatorTableCell<S> extends TableCell<S, Double> {
public static <S> Callback<TableColumn<S, Double>, TableCell<S, Double>> forTableColumn() {
return new Callback<TableColumn<S, Double>, TableCell<S, Double>>() {
#Override
public TableCell<S, Double> call(TableColumn<S, Double> param) {
return new ProgressIndicatorTableCell<>();
}
};
}
private final ProgressIndicator progressIndicator;
private ObservableValue observable;
public ProgressIndicatorTableCell() {
this.progressIndicator = new ProgressIndicator();
setGraphic(progressIndicator);
}
#Override
public void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
progressIndicator.progressProperty().unbind();
observable = getTableColumn().getCellObservableValue(getIndex());
if (observable != null) {
progressIndicator.progressProperty().bind(observable);
} else {
progressIndicator.setProgress(item);
}
setGraphic(progressIndicator);
}
}
}
And the current output:
Here is a version that implements your first question. With this requirement, the cell is only a function of the task's state. If it's RUNNING, display an indeterminate progress indicator; if it's SUCCEEDED display a progress indicator with value 1; otherwise, display nothing.
Note the original question is very old and uses a lot of outdated code styles. I've updated accordingly.
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ProgressIndicatorTableCellTest extends Application {
public void start(Stage primaryStage) {
TableView<TestTask> table = new TableView<>();
Random rng = new Random();
for (int i = 0; i < 3; i++) {
table.getItems().add(new TestTask(rng.nextInt(3000) + 2000, "Test"));
}
TableColumn<TestTask, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(data -> data.getValue().nameProperty());
nameCol.setPrefWidth(75);
TableColumn<TestTask, Worker.State> progressCol = new TableColumn<>("Progress");
progressCol.setCellValueFactory(data -> data.getValue().stateProperty());
progressCol.setCellFactory(col -> new ProgressIndicatorTableCell<>());
table.getColumns().addAll(nameCol, progressCol);
BorderPane root = new BorderPane();
root.setCenter(table);
Button btn = new Button("Start");
btn.setOnAction(actionEvent -> {
ExecutorService executor = Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
});
for (TestTask task : table.getItems()) {
executor.submit(task);
}
});
root.setBottom(btn);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public static class TestTask extends Task<Void> {
private final int waitTime; // milliseconds
final ReadOnlyStringWrapper name = new ReadOnlyStringWrapper();
public static final int NUM_ITERATIONS = 100;
public TestTask(int waitTime, String name) {
this.waitTime = waitTime;
this.name.set(name);
}
public ReadOnlyStringProperty nameProperty() {
return name.getReadOnlyProperty();
}
#Override
protected Void call() throws Exception {
this.updateProgress(ProgressIndicator.INDETERMINATE_PROGRESS, 1);
Thread.sleep(waitTime);
this.updateProgress(1, 1);
return null;
}
}
}
class ProgressIndicatorTableCell<S> extends TableCell<S, Worker.State> {
private final ProgressIndicator progressIndicator = new ProgressIndicator();
#Override
protected void updateItem(Worker.State state, boolean empty) {
super.updateItem(state, empty);
if (state == Worker.State.SUCCEEDED) {
progressIndicator.setProgress(1);
setGraphic(progressIndicator);
} else if (state == Worker.State.RUNNING) {
progressIndicator.setProgress(-1);
setGraphic(progressIndicator);
} else {
setGraphic(null);
}
}
}
To allow for "restarting", you should use a Service instead of just a Task. This version will allow for a restart if the button is pressed multiple times, returning everything to the initial state before proceeding.
This version also factors the processing work out of the model class, which is desirable for properly assigning responsibilities to classes:
Item.java:
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleObjectProperty;
public class Item {
public enum State {WAITING, PROCESSING, READY}
final ReadOnlyStringWrapper name = new ReadOnlyStringWrapper();
private final ObjectProperty<State> state = new SimpleObjectProperty<>(State.WAITING);
public Item(String name) {
this.name.set(name);
}
public ReadOnlyStringProperty nameProperty() {
return name.getReadOnlyProperty();
}
public State getState() {
return state.get();
}
public ObjectProperty<State> stateProperty() {
return state;
}
public void setState(State state) {
this.state.set(state);
}
}
ProcessManager.java:
import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import java.util.List;
import java.util.Random;
public class ProcessManager {
private final List<Item> items;
private Random rng = new Random();
private Service<Void> service = new Service<>() {
#Override
protected Task<Void> createTask() {
return new Task<>() {
#Override
protected Void call() throws Exception {
for (Item task: items) {
try {
Platform.runLater(() -> task.setState(Item.State.PROCESSING));
Thread.sleep(2000 + rng.nextInt(3000));
Platform.runLater(() -> task.setState(Item.State.READY));
} catch (InterruptedException exc) {
Thread.currentThread().interrupt();
}
if (isCancelled()) {
Platform.runLater(() -> task.setState(Item.State.WAITING));
break;
}
}
return null;
}
};
}
};
public ProcessManager(List<Item> items) {
this.items = items ;
service.setOnCancelled(e -> items.forEach(task -> task.setState(Item.State.WAITING)));
}
public void process() {
service.restart();
}
}
and the application:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class ProgressIndicatorTableCellTest extends Application {
public void start(Stage primaryStage) {
ObservableList<Item> tasks = FXCollections.observableArrayList();
ProcessManager processManager = new ProcessManager(tasks);
TableView<Item> table = new TableView<>();
for (int i = 0; i < 3; i++) {
Item task = new Item("Item " + (i + 1));
tasks.add(task);
}
table.setItems(tasks);
TableColumn<Item, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(data -> data.getValue().nameProperty());
nameCol.setPrefWidth(75);
TableColumn<Item, Item.State> progressCol = new TableColumn<>("Progress");
progressCol.setCellValueFactory(data -> data.getValue().stateProperty());
progressCol.setCellFactory(col -> new TableCell<>() {
private final ProgressIndicator indicator = new ProgressIndicator();
#Override
protected void updateItem(Item.State state, boolean empty) {
super.updateItem(state, empty);
if (state == Item.State.PROCESSING) {
indicator.setProgress(-1);
setGraphic(indicator);
} else if (state == Item.State.READY) {
indicator.setProgress(1);
setGraphic(indicator);
} else {
setGraphic(null);
}
}
});
table.getColumns().addAll(nameCol, progressCol);
BorderPane root = new BorderPane();
root.setCenter(table);
Button btn = new Button("Start");
btn.setOnAction(actionEvent -> processManager.process());
root.setBottom(btn);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

How to center the text in TreeTableView Column?

I've a JFXTreeTableView and i want to center the text of the data for each column.
there is one of my creating columns code :
JFXTreeTableColumn<TableData, String> DrinkColumn = new JFXTreeTableColumn<>("Drink");
DrinkColumn.setPrefWidth(100);
DrinkColumn.setCellValueFactory(new Callback<TreeTableColumn.CellDataFeatures<TableData, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TreeTableColumn.CellDataFeatures<TableData, String> param) {
return param.getValue().getValue().Drink;
}
}
);
I don't use JFoenix, but using a standard TreeTableView, the following external CSS will center the text in tree table cells:
.tree-table-cell {
-fx-alignment: center ;
}
Here's a SSCCE (the code above goes in style.css):
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
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.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.stage.Stage;
public class TreeTableViewTest extends Application {
#Override
public void start(Stage primaryStage) {
TreeTableView<Item> table = new TreeTableView<>();
TreeTableColumn<Item, String> col = new TreeTableColumn<>("Item");
col.setCellValueFactory(cellData -> cellData.getValue().getValue().nameProperty());
col.setPrefWidth(250);
table.getColumns().add(col);
TreeTableColumn<Item, Number> valueCol = new TreeTableColumn<>("Value");
valueCol.setCellValueFactory(cellData -> cellData.getValue().getValue().valueProperty());
valueCol.setPrefWidth(150);
table.getColumns().add(valueCol);
table.setRoot(createRandomTree(50));
Scene scene = new Scene(table);
scene.getStylesheets().add("style.css");
primaryStage.setScene(scene);
primaryStage.show();
}
private TreeItem<Item> createRandomTree(int nItems) {
Random rng = new Random();
TreeItem<Item> root = new TreeItem<>(new Item("Item 1", rng.nextInt(1000)));
root.setExpanded(true);
List<TreeItem<Item>> items = new ArrayList<>();
items.add(root);
for (int i = 2 ; i <= nItems ; i++) {
TreeItem<Item> item = new TreeItem<>(new Item("Item "+i, rng.nextInt(1000)));
item.setExpanded(true);
items.get(rng.nextInt(items.size())).getChildren().add(item);
items.add(item);
}
return root ;
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty value = new SimpleIntegerProperty();
public Item(String name, int value) {
setName(name);
setValue(value);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
public final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
}
public static void main(String[] args) {
launch(args);
}
}
If you want to center only specific columns, then use a cell factory on the column and set a CSS class or PseudoClass on the cell:
valueCol.setCellFactory(column -> {
TreeTableCell<Item, Number> cell = new TreeTableCell<Item, Number>() {
#Override
protected void updateItem(Number value, boolean empty) {
super.updateItem(value, empty);
if (empty) {
setText(null);
} else {
setText(value.toString());
}
}
};
cell.pseudoClassStateChanged(PseudoClass.getPseudoClass("centered"), true);
return cell ;
});
and modify the CSS accordingly:
.tree-table-cell:centered {
-fx-alignment: center ;
}
The latter version gives

JavaFX: How to get more "dummy/empty Rows"

I have a Javafx TableView where I can add new Rows by double Click on an empty Row at the End of my "filled" / Textfield filled Rows.
My Problem is,if i add some Rows ,Java don't give me more of the empty Rows I could double click to add some Rows.
Edit:removed some unnessary log
To see what i mean, here is the Code:
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextArea;
import javafx.util.Callback;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
interface inside_table
{
public void Select_Row_by_Col(int index);
}
public class Supermain extends Application {
ObservableList<myTextRow> data;
#Override
public void start(Stage primaryStage) {
ArrayList myindizes=new ArrayList();
final TableView<myTextRow> table = new TableView<>();
table.setEditable(true);
table.setStyle("-fx-text-wrap: true;");
//Table columns
TableColumn<myTextRow, String> clmID = new TableColumn<>("ID");
clmID.setMinWidth(160);
clmID.setCellValueFactory(new PropertyValueFactory<>("ID"));
TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));
clmtext.setCellFactory(new TextFieldCellFactory("text"));
TableColumn<myTextRow, String> clmtext2 = new TableColumn<>("Text2");
clmtext2.setMinWidth(160);
clmtext2.setCellValueFactory(new PropertyValueFactory<>("text2"));
clmtext2.setCellFactory(new TextFieldCellFactory("text2"));
//Add data
data = FXCollections.observableArrayList(
new myTextRow(5, "Lorem","bla"),
new myTextRow(2, "Ipsum","bla")
);
table.getColumns().addAll(clmID, clmtext,clmtext2);
table.setItems(data);
table.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getButton().equals(MouseButton.PRIMARY)) {
if (mouseEvent.getClickCount() == 2 && mouseEvent.getY()>24) {
data.add(new myTextRow(td_get_biggest_ID() + 1,"",""));
table.selectionModelProperty().get().select(data.size()-1);
}
}
}
});
HBox hBox = new HBox();
hBox.setSpacing(5.0);
hBox.setPadding(new Insets(5, 5, 5, 5));
Button btn = new Button();
btn.setText("Get Data");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
for (myTextRow data1 : data) {
System.out.println("data:" + data1.getText2());
}
}
});
hBox.getChildren().add(btn);
BorderPane pane = new BorderPane();
pane.setTop(hBox);
pane.setCenter(table);
primaryStage.setScene(new Scene(pane, 640, 480));
primaryStage.show();
class I_table implements inside_table{
#Override
public void Select_Row_by_Col(int index) {
table.getSelectionModel().select(index);
}
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public static class TextFieldCellFactory
implements Callback<TableColumn<myTextRow, String>, TableCell<myTextRow, String>> {
private String ColumnName;
public TextFieldCellFactory(String ColumnName){
this.ColumnName=ColumnName;
}
#Override
public TableCell<myTextRow, String> call(TableColumn<myTextRow, String> param) {
TextFieldCell textFieldCell = new TextFieldCell(this.ColumnName);
return textFieldCell;
}
public static class TextFieldCell extends TableCell<myTextRow, String> {
private TextArea textField;
private StringProperty boundToCurrently = null;
private String last_text;
private String ColumnName;
public TextFieldCell(String cname) {
textField = new TextArea();
textField.setWrapText(true);
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
last_text="";
this.ColumnName=cname;
this.setGraphic(textField);
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if(this.ColumnName=="text2"){
if(isNowFocused){last_text=textField.getText();System.out.println("NOW focus "+last_text);}
if (! isNowFocused && ! isValid(textField.getText())) {
textField.setText(last_text);
//textField.setText("00:00:00:00");
textField.selectAll();
System.out.println("blur");
}
}
});
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
// myindizes.add(getIndex());
// Retrieve the actual String Property that should be bound to the TextField
// If the TextField is currently bound to a different StringProperty
// Unbind the old property and rebind to the new one
ObservableValue<String> ov = getTableColumn().getCellObservableValue(getIndex());
SimpleStringProperty sp = (SimpleStringProperty) ov;
if (this.boundToCurrently == null) {
this.boundToCurrently = sp;
this.textField.textProperty().bindBidirectional(sp);
} else if (this.boundToCurrently != sp) {
this.textField.textProperty().unbindBidirectional(this.boundToCurrently);
this.boundToCurrently = sp;
this.textField.textProperty().bindBidirectional(this.boundToCurrently);
}
double height = real_lines_height(textField.getText(), this.getWidth(), 30, 22);
textField.setPrefHeight(height);
textField.setMaxHeight(height);
textField.setMaxHeight(Double.MAX_VALUE);
// if height bigger than the biggest height in the row
//-> change all heights of the row(textfields ()typeof textarea) to this height
// else leave the height as it is
//System.out.println("item=" + item + " ObservableValue<String>=" + ov.getValue());
//this.textField.setText(item); // No longer need this!!!
} else {
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}//update
private boolean isValid(String s){
String splitArray[] = s.split(":");
if (splitArray.length != 4) {
System.out.println("false");
return false;
}
for (int i = 0; i < splitArray.length; i++) {
if (splitArray[i].length() != 2) {
System.out.println("false");
return false;
}
if (!splitArray[i].substring(0, 1).matches("[0-9]")) {
System.out.println("no number1");
return false;
}
if (!splitArray[i].substring(1, 2).matches("[0-9]")) {
System.out.println("no number2");
return false;
}
if (i < 3) {
int itest = Integer.parseInt(splitArray[i]);
if (itest > 59) {
System.out.println(itest + " ist zu groß!");
return false;
}
} else {
int itest2 = Integer.parseInt(splitArray[i]);
if (itest2 > Math.floor(25)) {
System.out.println(itest2 + " ist zu groß!");
return false;
}
//framerate!!!!!
}
System.out.println("splits: " + splitArray[i]);
//if( el.charAt(0).)
}
return true;
}
}
}
public class myTextRow {
private final SimpleIntegerProperty ID;
private final SimpleStringProperty text;
private final SimpleStringProperty text2;
public myTextRow(int ID, String text,String text2) {
this.ID = new SimpleIntegerProperty(ID);
this.text = new SimpleStringProperty(text);
this.text2 = new SimpleStringProperty(text2);
}
//setter
public void setID(int id) {
this.ID.set(id);
}
public void setText(String text) {
this.text.set(text);
}
public void setText2(String text) {
this.text2.set(text);
}
//getter
public int getID() {
return ID.get();
}
public String getText() {
return text.get();
}
public String getText2() {
return text2.get();
}
//properties
public StringProperty textProperty() {
return text;
}
public StringProperty text2Property() {
return text2;
}
public IntegerProperty IDProperty() {
return ID;
}
}
private static double real_lines_height(String s, double width, double heightCorrector, double widthCorrector) {
HBox h = new HBox();
Label l = new Label("Text");
h.getChildren().add(l);
Scene sc = new Scene(h);
l.applyCss();
double line_height = l.prefHeight(-1);
int new_lines = s.replaceAll("[^\r\n|\r|\n]", "").length();
// System.out.println("new lines= "+new_lines);
String[] lines = s.split("\r\n|\r|\n");
// System.out.println("line count func= "+ lines.length);
int count = 0;
//double rest=0;
for (int i = 0; i < lines.length; i++) {
double text_width = get_text_width(lines[i]);
double plus_lines = Math.ceil(text_width / (width - widthCorrector));
if (plus_lines > 1) {
count += plus_lines;
//rest+= (text_width / (width-widthCorrector)) - plus_lines;
} else {
count += 1;
}
}
//count+=(int) Math.ceil(rest);
count += new_lines - lines.length;
return count * line_height + heightCorrector;
}
private static double get_text_width(String s) {
HBox h = new HBox();
Label l = new Label(s);
l.setWrapText(false);
h.getChildren().add(l);
Scene sc = new Scene(h);
l.applyCss();
// System.out.println("FXMLDocumentController.get_text_width(): "+l.prefWidth(-1));
return l.prefWidth(-1);
}
public int td_get_biggest_ID() {
int biggest = 0;
for (int i = 0; i < data.size(); i++) {
if (((myTextRow) data.get(i)).getID() > biggest) {
biggest = ((myTextRow) data.get(i)).getID();
}
}
return biggest;
}
}
Just click anywhere else on the TableView but make sure it's at least 24 pixels from the top; This will work since you've added the event handler is added to the TableView...
If you only want to use the last row, then use a custom rowFactory and handle the events there.
Add a placeholder item to the TableView items that marks the row that is used for adding new elements (for some reason the selection model doesn't like null):
final myTextRow addPlaceHolder = new myTextRow(Integer.MIN_VALUE, null, null);
...
//Add data
data = FXCollections.observableArrayList(
new myTextRow(5, "Lorem", "bla"),
new myTextRow(2, "Ipsum", "bla"),
addPlaceHolder
);
make sure your TextFieldCells treat null values as empty rows:
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
...
make sure the first column does not display anything for the placeholder
//Table columns
TableColumn<myTextRow, Number> clmID = new TableColumn<>("ID");
clmID.setMinWidth(160);
clmID.setCellValueFactory(cdf -> {
myTextRow item = cdf.getValue();
return item == addPlaceHolder ? Bindings.createObjectBinding(() -> null) : item.IDProperty();
});
and use the following rowFactory to handle adding the items (you don't need the updateItem part unless you need to add a style class to the TableRow; you need not extend TableRow in this case)
table.setRowFactory(tv -> new TableRow<myTextRow>() {
{
setOnMouseClicked(mouseEvent -> {
if (mouseEvent.getButton() == MouseButton.PRIMARY
&& mouseEvent.getClickCount() == 2
&& !isEmpty()
&& getItem() == addPlaceHolder) {
data.add(data.size() - 1, new myTextRow(td_get_biggest_ID() + 1, "", ""));
table.selectionModelProperty().get().select(data.size() - 1);
mouseEvent.consume();
}
});
}
#Override
protected void updateItem(myTextRow item, boolean empty) {
super.updateItem(item, empty);
// add style class for row containing addPlaceHolder
List<String> classes = getStyleClass();
final String clazz = "add-row";
if (item == addPlaceHolder) {
if (!classes.contains(clazz)) {
classes.add(clazz);
}
} else {
classes.remove(clazz);
}
}
});

TreeTableView JavaFX 8: I need to make one of columns to use TextFIeld and ChoiceBox when editing

I have TreeTableView with 2 columns, so I want to be able something like that:
user double click in cell -> Someclass.getType() returns type of editing field ->in cell I see this type of editing field(TextField or ChoiceBox)
wnen I need to use TextField only, i can use someshing like that
TreeColumn1.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
TreeColumn1.setOnEditCommit(firstColumnCommitHandler);
commitHandler:
private EventHandler<TreeTableColumn.CellEditEvent<SomeClass, String>> firstColumnCommitHandler = event -> {
final SomeClass item = event.getRowValue().getValue();
item.setVariable(event.getNewValue());
};
but i need different types, and have no idea howto do this
For this you need to implement the table cell yourself, and display the appropriate components when you go in and out of editing state. Here's a basic idea. The ChoiceBoxs look odd, you may need to work with some CSS to get them looking correct. In this example, if the box in the first column is checked, the second column will use a ChoiceBox for editing; otherwise it will use a TextField.
import java.util.function.Function;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TableWithVaryingEditor extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
table.setEditable(true);
IntStream.rangeClosed(1, 20).mapToObj(i -> new Item("Item "+i)).forEach(table.getItems()::add);
TableColumn<Item, Boolean> fixedCol = column("Fixed", Item::fixedProperty);
table.getColumns().add(fixedCol);
fixedCol.setCellFactory(CheckBoxTableCell.forTableColumn(fixedCol));
TableColumn<Item, String> nameCol = column("Name", Item::nameProperty);
table.getColumns().add(nameCol);
nameCol.setCellFactory(col -> new TableCell<Item, String>() {
private TextField textField = new TextField();
private ChoiceBox<String> choice = new ChoiceBox<>();
private boolean ignoreChoiceBoxChange = false ;
// anonymous constructor:
{
choice.valueProperty().addListener((obs, oldValue, newValue) -> {
if (! ignoreChoiceBoxChange) {
commitEdit(newValue);
}
});
choice.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (! isNowFocused) {
cancelEdit();
}
});
choice.showingProperty().addListener((obs, wasShowing, isNowShowing) -> {
if (! isNowShowing) {
cancelEdit();
}
});
textField.setOnAction(e -> commitEdit(textField.getText()));
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (! isNowFocused) {
cancelEdit();
}
});
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (isEditing()) {
updateEditor();
} else {
updateText();
}
}
#Override
public void startEdit() {
super.startEdit();
updateEditor();
}
#Override
public void cancelEdit() {
super.cancelEdit();
updateText();
}
#Override
public void commitEdit(String item) {
super.commitEdit(item);
updateText();
}
private void updateEditor() {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
int index = getIndex();
Item item = getTableView().getItems().get(index);
if (item.isFixed()) {
ignoreChoiceBoxChange = true ;
choice.getItems().setAll(getItem(), "Choice 1", "Choice 2");
choice.getSelectionModel().select(getItem());
setGraphic(choice);
choice.show();
ignoreChoiceBoxChange = false ;
} else {
textField.setText(getItem());
setGraphic(textField);
}
}
private void updateText() {
setContentDisplay(ContentDisplay.TEXT_ONLY);
if (isEmpty()) {
setText(null);
} else {
setText(getItem());
}
}
});
primaryStage.setScene(new Scene(new BorderPane(table), 600, 400));
primaryStage.show();
}
private <S,T> TableColumn<S,T> column(String title, Function<S, ObservableValue<T>> property) {
TableColumn<S,T> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
return col ;
}
public static class Item {
private final BooleanProperty fixed = new SimpleBooleanProperty();
private final StringProperty name = new SimpleStringProperty();
public Item(String name) {
setName(name);
}
public final BooleanProperty fixedProperty() {
return this.fixed;
}
public final boolean isFixed() {
return this.fixedProperty().get();
}
public final void setFixed(final boolean fixed) {
this.fixedProperty().set(fixed);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
}
public static void main(String[] args) {
launch(args);
}
}

Detect doubleclick on row of TableView JavaFX

I need to detect double clicks on a row of a TableView.
How can I listen for double clicks on any part of the row and get all data of this row to print it to the console?
TableView<MyType> table = new TableView<>();
//...
table.setRowFactory( tv -> {
TableRow<MyType> row = new TableRow<>();
row.setOnMouseClicked(event -> {
if (event.getClickCount() == 2 && (! row.isEmpty()) ) {
MyType rowData = row.getItem();
System.out.println(rowData);
}
});
return row ;
});
Here is a complete working example:
import java.util.Random;
import java.util.function.Function;
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.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class TableViewDoubleClickOnRow extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
table.setRowFactory(tv -> {
TableRow<Item> row = new TableRow<>();
row.setOnMouseClicked(event -> {
if (event.getClickCount() == 2 && (! row.isEmpty()) ) {
Item rowData = row.getItem();
System.out.println("Double click on: "+rowData.getName());
}
});
return row ;
});
table.getColumns().add(column("Item", Item::nameProperty));
table.getColumns().add(column("Value", Item::valueProperty));
Random rng = new Random();
for (int i = 1 ; i <= 50 ; i++) {
table.getItems().add(new Item("Item "+i, rng.nextInt(1000)));
}
Scene scene = new Scene(table);
primaryStage.setScene(scene);
primaryStage.show();
}
private static <S,T> TableColumn<S,T> column(String title, Function<S, ObservableValue<T>> property) {
TableColumn<S,T> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
return col ;
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty value = new SimpleIntegerProperty();
public Item(String name, int value) {
setName(name);
setValue(value);
}
public StringProperty nameProperty() {
return name ;
}
public final String getName() {
return nameProperty().get();
}
public final void setName(String name) {
nameProperty().set(name);
}
public IntegerProperty valueProperty() {
return value ;
}
public final int getValue() {
return valueProperty().get();
}
public final void setValue(int value) {
valueProperty().set(value);
}
}
public static void main(String[] args) {
launch(args);
}
}
Example:
table.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.isPrimaryButtonDown() && event.getClickCount() == 2) {
System.out.println(table.getSelectionModel().getSelectedItem());
}
}
});
If you are using custom selection model, then you can get the row from event, example:
table.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.isPrimaryButtonDown() && event.getClickCount() == 2) {
Node node = ((Node) event.getTarget()).getParent();
TableRow row;
if (node instanceof TableRow) {
row = (TableRow) node;
} else {
// clicking on text part
row = (TableRow) node.getParent();
}
System.out.println(row.getItem());
}
}
});
This works for me:
table.setOnMouseClicked((MouseEvent event) -> {
if (event.getButton().equals(MouseButton.PRIMARY) && event.getClickCount() == 2){
System.out.println(table.getSelectionModel().getSelectedItem());
}
});
}
If you are using SceneBuilder you can set your table's OnMouseClicked to handleRowSelect() method as shown below:
MyType temp;
Date lastClickTime;
#FXML
private void handleRowSelect() {
MyType row = myTableView.getSelectionModel().getSelectedItem();
if (row == null) return;
if(row != temp){
temp = row;
lastClickTime = new Date();
} else if(row == temp) {
Date now = new Date();
long diff = now.getTime() - lastClickTime.getTime();
if (diff < 300){ //another click registered in 300 millis
System.out.println("Edit dialog");
} else {
lastClickTime = new Date();
}
}
}
Extending the previous answer:
The extra check ensures the selected row was double clicked - ignoring double clicks on empty rows or the column header
table.setRowFactory(param -> {
TableRow<MyType> row = new TableRow<>();
row.setOnMouseClicked(event -> Optional.ofNullable(row.getItem()).ifPresent(rowData-> {
if(event.getClickCount() == 2 && rowData.equals(table.getSelectionModel().getSelectedItem())){
System.out.println(rowData);
}
}));
return row;
});
```
This answer has been tested:
table.setOnMouseClicked( event -> {
if( event.getClickCount() == 2 ) {
System.out.println( table.getSelectionModel().getSelectedItem());
}});
table.getSelectionModel().getSelectedItem() can be use since we catch a double-click. One the first click the selection moves, on the second this handler is executed.
I had similar situation not to detect mouse double click event on TableView.
Above all samples worked perfectly. but my application did not detect double click event at all.
But I found that if TableView is on editable, mouse double click event can not be detected !!
check your application if TableView is on editable like this.
tableView.setEditable( true );
if then, double click event only raises on same row selected.

Categories