Last active
September 23, 2020 11:22
-
-
Save jaakkju/2c404a5891fa74817af9 to your computer and use it in GitHub Desktop.
Changing JavaFX table view row styling with non-visible property using row level property binding.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import javafx.beans.property.SimpleBooleanProperty; | |
/** | |
* Simple abstract class that has active boolean property | |
* | |
* @author jaakkju | |
*/ | |
abstract public class AbstractToggleTableItem { | |
private final SimpleBooleanProperty active = new SimpleBooleanProperty(true); | |
public AbstractToggleTableItem(boolean active) { | |
this.active.set(active); | |
} | |
public SimpleBooleanProperty activeProperty() { | |
return active; | |
} | |
public void setActive(boolean active) { | |
this.active.set(active); | |
} | |
public boolean isActive() { | |
return active.get(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@CHARSET "UTF-8"; | |
.table-row-cell:disabled { | |
-fx-background-color: #c1c1c1; | |
-fx-control-inner-background: #c1c1c1; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Arrays; | |
import java.util.Random; | |
import javafx.application.Application; | |
import javafx.beans.property.SimpleStringProperty; | |
import javafx.beans.property.StringProperty; | |
import javafx.collections.FXCollections; | |
import javafx.collections.ObservableList; | |
import javafx.event.ActionEvent; | |
import javafx.geometry.Insets; | |
import javafx.geometry.Pos; | |
import javafx.scene.Scene; | |
import javafx.scene.control.Button; | |
import javafx.scene.control.Label; | |
import javafx.scene.control.SelectionMode; | |
import javafx.scene.control.TableColumn; | |
import javafx.scene.control.TableView; | |
import javafx.scene.control.cell.PropertyValueFactory; | |
import javafx.scene.layout.HBox; | |
import javafx.scene.layout.VBox; | |
import javafx.scene.text.Font; | |
import javafx.stage.Stage; | |
public class TableViewRowStyleSample extends Application { | |
private final TableView<Person> table = new TableView<>(); | |
private final ObservableList<Person> rows = | |
FXCollections.observableArrayList( | |
new Person("Jacob", "Smith", "[email protected]"), | |
new Person("Isabella", "Johnson", "[email protected]"), | |
new Person("Ethan", "Williams", "[email protected]"), | |
new Person("Emma", "Jones", "[email protected]"), | |
new Person("Michael", "Brown", "[email protected]") | |
); | |
public static void main(String[] args) { | |
launch(args); | |
} | |
@Override | |
public void start(Stage stage) { | |
final Label label = new Label("Address Book"); | |
label.setFont(new Font("Arial", 20)); | |
table.setEditable(true); | |
TableColumn<Person, String> tcFirstName = new TableColumn<>("First Name"); | |
tcFirstName.setMinWidth(100); | |
tcFirstName.setCellValueFactory(new PropertyValueFactory<>("firstName")); | |
TableColumn<Person, String> tcLastName = new TableColumn<>("Last Name"); | |
tcLastName.setMinWidth(100); | |
tcLastName.setCellValueFactory(new PropertyValueFactory<>("lastName")); | |
TableColumn<Person, String> tcEmail = new TableColumn<>("Email"); | |
tcEmail.setMinWidth(200); | |
tcEmail.setCellValueFactory(new PropertyValueFactory<>("email")); | |
table.setItems(rows); | |
table.getColumns().addAll(Arrays.asList(tcFirstName, tcLastName, tcEmail)); | |
table.setRowFactory(p -> new ToggleTableRow<>()); | |
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); | |
final Button btnDisable = new Button("Disable random"); | |
btnDisable.setOnAction((ActionEvent event) -> { | |
/* Randomly select one row and change it's active value */ | |
Random generator = new Random(); | |
int i = generator.nextInt(rows.size()); | |
Person item = rows.get(i); | |
if(item.isActive()) { | |
item.setActive(false); | |
} else { | |
item.setActive(true); | |
} | |
}); | |
final HBox buttons = new HBox(5); | |
buttons.setAlignment(Pos.CENTER); | |
buttons.getChildren().addAll(btnDisable); | |
final VBox vbox = new VBox(); | |
vbox.setSpacing(5); | |
vbox.setPadding(new Insets(10, 0, 0, 10)); | |
vbox.getChildren().addAll(label, table, buttons); | |
Scene scene = new Scene(vbox, 450, 500); | |
stage.setTitle("JavaFX table row style update with non-visible property"); | |
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm()); | |
stage.setScene(scene); | |
stage.show(); | |
} | |
public static class Person extends AbstractToggleTableItem { | |
private final StringProperty firstName; | |
private final StringProperty lastName; | |
private final StringProperty email; | |
private Person(String fName, String lName, String email) { | |
super(true); | |
this.firstName = new SimpleStringProperty(this, "firstName", fName); | |
this.lastName = new SimpleStringProperty(this, "lastName", lName); | |
this.email = new SimpleStringProperty(this, "email", email); | |
} | |
public final String getFirstName() { | |
return firstName.get(); | |
} | |
public final void setFirstName(String fName) { | |
firstName.set(fName); | |
} | |
public StringProperty firstNameProperty() { | |
return firstName ; | |
} | |
public final String getLastName() { | |
return lastName.get(); | |
} | |
public final void setLastName(String fName) { | |
lastName.set(fName); | |
} | |
public final StringProperty lastNameProperty() { | |
return lastName ; | |
} | |
public final String getEmail() { | |
return email.get(); | |
} | |
public final void setEmail(String fName) { | |
email.set(fName); | |
} | |
public final StringProperty emailProperty() { | |
return email ; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import javafx.beans.property.SimpleBooleanProperty; | |
import javafx.beans.value.ObservableValue; | |
import javafx.scene.control.TableRow; | |
/** | |
* Item change makes binding between table item and table row, so that we can force | |
* UpdateItem call when active property changes. We don't | |
* have to force the update it's skin with visibility true/false | |
* trick. ex. | |
* | |
* ((TableColumn) getTableView().getColumns().get(0)).setVisible(false); | |
* ((TableColumn) getTableView().getColumns().get(0)).setVisible(true); | |
* | |
* Binding created is a weak reference so garbage collection should work properly. | |
* | |
* @author jaakkju | |
* @param <T> | |
*/ | |
public class ToggleTableRow<T extends AbstractToggleTableItem> extends TableRow<T> { | |
private final SimpleBooleanProperty active = new SimpleBooleanProperty(); | |
/* Current Item which is bound to this table row */ | |
private T currentItem = null; | |
public ToggleTableRow() { | |
super(); | |
active.addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> { | |
/* If item is the same we know that the update came | |
from actual property change and not from the row reuse */ | |
if (currentItem != null && currentItem == getItem()) { | |
updateItem(getItem(), isEmpty()); | |
} | |
}); | |
/* | |
JavaFX reuses rows in the same way as it reuses table cells, | |
item behind the row changes ex. if row if scrolled so that it is not visible. | |
*/ | |
itemProperty().addListener((ObservableValue<? extends T> observable, T oldValue, T newValue) -> { | |
/* When the item changes, we unbind the | |
old property and start listening to the new */ | |
active.unbind(); | |
if (newValue != null) { | |
active.bind(newValue.activeProperty()); | |
/* We change current item only after | |
binding since since it trickers change in the properties */ | |
currentItem = newValue; | |
} | |
}); | |
} | |
@Override | |
final protected void updateItem(T item, boolean empty) { | |
super.updateItem(item, empty); | |
/* Setting disabled sets row's pseudoclass to disabled, we can use that | |
value to assign inactive styling to the row. */ | |
setDisable(item != null && !item.isActive()); | |
setEditable(item != null && !item.isActive()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you very much, this works perfectly!
⚡