Update: inserted project (reproductible example) and last details
I have an application that is displaying a TableView filled with simple objects (observable list)
I want to display selected items (rows) in a TableView highlighting them.
Ex: If the user press 'Insert' i update (in the observable list) the object which is selected. A boolean in the object will do. The objects are 'marked'; the user can do something else.
I cannot use myTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
as the user will loose the selection as soon as a key is pressed or a mouse clik happens.
With that in mind, this means i'm managing keyboard like so:
public boolean implementListenerPackage(Scene s) {
//some init then...
s.setOnKeyReleased(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent ke) {
switch (ke.getCode()) {
case INSERT:
setObservableListObjectSelect();
break;
}
}
});
}
The object in the observable list is rather simple:
public class myObject {
private boolean selected;
private String otherStuff = "";
// Then constructor , getters and setters
And i have a MouseEvent management to handle other actions as well. When i'm creating my TableView i add this:
myTableView.setRowFactory(rftv-> {
TableRow<type> rowObj = new TableRow<>();
rowObj.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
if (e.getClickCount() == 2 && (!rowObj.isEmpty())) {
SomeClass.doSomethingForDoubleClik()
} else { // Simple clic
SomeClass.doSomethingForSimpleClik()
}
}
});
return rowObj;
});
My goal is to change the CSS of a row when the myObject boolean changes. And by doing so make the user selection highlighted even though the user click on another row.
I tried :
- A lot of reading on this website, but only to find simple examples. Not the one i have with several obstacles at the same time.
- to implement more things in the rowFactory. But i couldn't do it. If it compiles, then it crashes with a nullpointerexception. I never been good at those tricky syntax things.
- to do it directly from the keyboard management but only to find it's rather complicated. I have to get the selected object, update it, then find the selected cells and change the CSS one by one (column logic).
- to implement a "binding" (An example here) between the object and the row but only to find myself wondering how to implement it as it was an answer for a different problem 'flavor'.
It's probably in front of my eyes but i can't see it.
update: Bear in mind that
- the keyboard management is centralized.
- there is already a factory set on the tableView.
- They are several TableView in the original application so i whish the CSS style change automatically and not in some kind of 'hardcoded' fashion.
The minimal code:
package application;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
public class Main extends Application {
Label lbl01 = new Label("Information");
@Override
public void start(Stage primaryStage) {
try {
TableView tv1 = new TableView();
TableColumn<MyObject, String> column1 = new TableColumn<>("Col 01");
column1.setCellValueFactory(new PropertyValueFactory<>("keyboardSelected"));
TableColumn<MyObject, String> column2 = new TableColumn<>("Col 02");
column2.setCellValueFactory(new PropertyValueFactory<>("dataA"));
TableColumn<MyObject, String> column3 = new TableColumn<>("Col 03");
column3.setCellValueFactory(new PropertyValueFactory<>("dataB"));
TableColumn<MyObject, String> column4 = new TableColumn<>("Col 04");
column4.setCellValueFactory(new PropertyValueFactory<>("dataC"));
tv1.getColumns().add(column1);
tv1.getColumns().add(column2);
tv1.getColumns().add(column3);
tv1.getColumns().add(column4);
ObservableList<MyObject> olm1 = FXCollections.observableArrayList();
olm1.addAll(new MyObject(false, "Object01 A", "Object01 B", "Object01 C"),
new MyObject(false, "Object02 A", "Object02 B", "Object02 C"),
new MyObject(false, "Object03 A", "Object03 B", "Object03 C"),
new MyObject(false, "Object04 A", "Object04 B", "Object04 C"),
new MyObject(false, "Object05 A", "Object05 B", "Object05 C")
);
tv1.setItems(olm1);
tv1.setRowFactory(dc -> {
TableRow<MyObject> rowObj = new TableRow<>();
rowObj.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
if (e.getClickCount() == 2 && (!rowObj.isEmpty())) {
lbl01.setText("Double click on line " + tv1.getSelectionModel().getSelectedIndex());
} else {
lbl01.setText("Single click on line " + +tv1.getSelectionModel().getSelectedIndex());
}
}
});
return rowObj;
});
VBox root = new VBox(tv1, lbl01);
Scene scene = new Scene(root, 512, 640);
primaryStage.setScene(scene);
KeyboardManagement km = new KeyboardManagement();
km.implementListener(scene, lbl01, tv1);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
package application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
public class KeyboardManagement {
public KeyboardManagement() {
}
public boolean implementListener(Scene s, Label l, TableView tv1) {
boolean retRep = false;
try {
s.setOnKeyReleased(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent ke) {
if (ke.getCode() == KeyCode.SPACE) {
MyObject m1 = (MyObject) tv1.getSelectionModel().getSelectedItem();
tv1.refresh();
m1.setKeyboardSelected(!m1.isKeyboardSelected());
l.setText("Space was pressed / TableView Line :" + tv1.getSelectionModel().getSelectedIndex()
+ ". " + m1.toString());
}
}
});
} catch (Exception e) {
}
return retRep;
}
}
package application;
public class MyObject {
private boolean keyboardSelected;
private String dataA;
private String dataB;
private String dataC;
public MyObject(boolean keyboardSelected, String dataA, String dataB, String dataC) {
super();
this.keyboardSelected = keyboardSelected;
this.dataA = dataA;
this.dataB = dataB;
this.dataC = dataC;
}
public boolean isKeyboardSelected() {
return keyboardSelected;
}
public void setKeyboardSelected(boolean keyboardSelected) {
this.keyboardSelected = keyboardSelected;
}
public String getDataA() {
return dataA;
}
public void setDataA(String dataA) {
this.dataA = dataA;
}
public String getDataB() {
return dataB;
}
public void setDataB(String dataB) {
this.dataB = dataB;
}
public String getDataC() {
return dataC;
}
public void setDataC(String dataC) {
this.dataC = dataC;
}
@Override
public String toString() {
return "MyObject [keyboardSelected=" + keyboardSelected + ", dataA=" + dataA + ", dataB=" + dataB + ", dataC="
+ dataC + "]";
}
}