Reputation: 3
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 :
It's probably in front of my eyes but i can't see it.
update: Bear in mind that
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 + "]";
}
}
Upvotes: 0
Views: 69
Reputation: 3
Found it!
Simple and complex at the same time.
The trick is to insert at the right place a listener on the object property.
Something like :
rowObj.itemProperty().addListener((observable, oldValue, newValue) -> updateTableRowCss(rowObj, newValue));
Notes:
You can add several listeners on different object properties.
I strongly suggest you create a method the listener will call. Sometimes the IDE syntax analyzers are making bloodshed in the screen. It doesn't help finding errors in this type of code.
From the tests i made, it seems that the modifications on the style you do will preval (like more important) on the already loaded style. Which is fine.
This way you keep a centralized keyboard management (like in this example) and the mouse event management in the factory.
The cursor still moves freely and you keep an object selection you can reuse later.
The modified example code from above:
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.itemProperty().addListener((observable, oldValue, newValue) -> updateTableRowCss(rowObj, newValue));
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);
}
private void updateTableRowCss(TableRow<MyObject> rowObj, MyObject item) {
// On doit vérifier si null
if (item != null ) {
if (item.isKeyboardSelected()) {
rowObj.setStyle("-fx-background-color: #FF000080;");
} else {
rowObj.setStyle("");
}
}
}
}
Upvotes: 0