Last active
May 22, 2021 14:31
-
-
Save james-d/7813134 to your computer and use it in GitHub Desktop.
Example of a row factory wrapper for TableViews which manages adding and removing a style class from a list of row indexes.
This file contains 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"; | |
.highlightedRow { | |
-fx-background-color: brown; | |
-fx-background-insets: 0, 1, 2; | |
-fx-background: -fx-accent; | |
-fx-text-fill: -fx-selection-bar-text; | |
} |
This file contains 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 javafx.application.Application; | |
import javafx.beans.binding.Bindings; | |
import javafx.beans.property.SimpleStringProperty; | |
import javafx.beans.property.StringProperty; | |
import javafx.collections.FXCollections; | |
import javafx.collections.ObservableList; | |
import javafx.event.ActionEvent; | |
import javafx.event.EventHandler; | |
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 HighlightingTableViewSample extends Application { | |
private TableView<Person> table = new TableView<Person>(); | |
private final ObservableList<Person> data = | |
FXCollections.observableArrayList( | |
new Person("Jacob", "Smith", "jacob.smith@example.com"), | |
new Person("Isabella", "Johnson", "isabella.johnson@example.com"), | |
new Person("Ethan", "Williams", "ethan.williams@example.com"), | |
new Person("Emma", "Jones", "emma.jones@example.com"), | |
new Person("Michael", "Brown", "michael.brown@example.com") | |
); | |
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> firstNameCol = new TableColumn<>("First Name"); | |
firstNameCol.setMinWidth(100); | |
firstNameCol.setCellValueFactory( | |
new PropertyValueFactory<Person, String>("firstName")); | |
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name"); | |
lastNameCol.setMinWidth(100); | |
lastNameCol.setCellValueFactory( | |
new PropertyValueFactory<Person, String>("lastName")); | |
TableColumn<Person, String> emailCol = new TableColumn<>("Email"); | |
emailCol.setMinWidth(200); | |
emailCol.setCellValueFactory( | |
new PropertyValueFactory<Person, String>("email")); | |
table.setItems(data); | |
table.getColumns().addAll(Arrays.asList(firstNameCol, lastNameCol, emailCol)); | |
final StyleChangingRowFactory<Person> rowFactory = new StyleChangingRowFactory<>("highlightedRow"); | |
table.setRowFactory(rowFactory); | |
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); | |
final Button highlightButton = new Button("Highlight"); | |
highlightButton.disableProperty().bind(Bindings.isEmpty(table.getSelectionModel().getSelectedIndices())); | |
highlightButton.setOnAction(new EventHandler<ActionEvent>() { | |
@Override | |
public void handle(ActionEvent event) { | |
rowFactory.getStyledRowIndices().setAll(table.getSelectionModel().getSelectedIndices()); | |
} | |
}); | |
final Button clearHighlightButton = new Button("Clear Highlights"); | |
clearHighlightButton.disableProperty().bind(Bindings.isEmpty(rowFactory.getStyledRowIndices())); | |
clearHighlightButton.setOnAction(new EventHandler<ActionEvent>() { | |
@Override | |
public void handle(ActionEvent event) { | |
rowFactory.getStyledRowIndices().clear(); | |
} | |
}); | |
final HBox buttons = new HBox(5); | |
buttons.setAlignment(Pos.CENTER); | |
buttons.getChildren().addAll(highlightButton, clearHighlightButton); | |
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("Highlighting Table View Sample"); | |
scene.getStylesheets().add(getClass().getResource("highlightingTable.css").toExternalForm()); | |
stage.setScene(scene); | |
stage.show(); | |
} | |
public static class Person { | |
private final StringProperty firstName; | |
private final StringProperty lastName; | |
private final StringProperty email; | |
private Person(String fName, String lName, String email) { | |
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 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.Collections; | |
import javafx.beans.value.ChangeListener; | |
import javafx.beans.value.ObservableValue; | |
import javafx.collections.FXCollections; | |
import javafx.collections.ListChangeListener; | |
import javafx.collections.ObservableList; | |
import javafx.scene.control.TableRow; | |
import javafx.scene.control.TableView; | |
import javafx.util.Callback; | |
/** | |
* A factory for <code>TableRow</code>s suitable for passing to | |
* <code>TableView.setRowFactory(...)</code>. This row factory will | |
* set a style class on rows whose indices are contained in | |
* <code>getStyledRowIndices</code>. | |
* Modifications to the <code>ObservableList<Integer></code> | |
* returned from <code>getStyledRowIndices</code> will result in | |
* immediate updates to the style classes of rows added to or removed | |
* from the list. | |
* The style class is determined | |
* by the <code>styleClass</code> parameter in the constructor. | |
* <br/> | |
* Usage example: | |
* <br/> | |
* <pre> | |
* final TableView<MyDomainObject> table = ... ; | |
* final StyleChangingRowFactory<MyDomainObject> rowFactory | |
* = new StyleChangingRowFactory<>("highlightedRow"); | |
* table.setRowFactory(rowFactory); | |
* | |
* Button highlightButton = new Button("Highlight selected rows"); | |
* highlightButton.setOnAction(new EventHandler<ActionEvent>() { | |
* @Override | |
* public void handle(ActionEvent event) { | |
* rowFactory().getStyledRowIndices() | |
* .setAll(table.getSelectionModel().getSelectedIndices()); | |
* } | |
* }); | |
* </pre> | |
* | |
* @author James_D | |
* | |
* @param <T> The type of the data displayed in the table row. | |
*/ | |
public class StyleChangingRowFactory<T> implements | |
Callback<TableView<T>, TableRow<T>> { | |
private final String styleClass ; | |
private final ObservableList<Integer> styledRowIndices ; | |
private final Callback<TableView<T>, TableRow<T>> baseFactory ; | |
/** | |
* Construct a <code>StyleChangingRowFactory</code>, | |
* specifying the name of the style class that will be applied | |
* to rows determined by <code>getStyledRowIndices</code> | |
* and a base factory to create the <code>TableRow</code>. If <code>baseFactory</code> | |
* is <code>null</code>, default table rows will be created. | |
* @param styleClass The name of the style class that will be applied to specified rows. | |
* @param baseFactory A factory for creating the rows. If null, default | |
* <code>TableRow<T></code>s will be created using the default <code>TableRow</code> constructor. | |
*/ | |
public StyleChangingRowFactory(String styleClass, Callback<TableView<T>, TableRow<T>> baseFactory) { | |
this.styleClass = styleClass ; | |
this.baseFactory = baseFactory ; | |
this.styledRowIndices = FXCollections.observableArrayList(); | |
} | |
/** | |
* Construct a <code>StyleChangingRowFactory</code>, | |
* which applies <code>styleClass</code> to the rows determined by | |
* <code>getStyledRowIndices</code>, and using default <code>TableRow</code>s. | |
* @param styleClass | |
*/ | |
public StyleChangingRowFactory(String styleClass) { | |
this(styleClass, null); | |
} | |
@Override | |
public TableRow<T> call(TableView<T> tableView) { | |
final TableRow<T> row ; | |
if (baseFactory == null) { | |
row = new TableRow<>(); | |
} else { | |
row = baseFactory.call(tableView); | |
} | |
row.indexProperty().addListener(new ChangeListener<Number>() { | |
@Override | |
public void changed(ObservableValue<? extends Number> obs, | |
Number oldValue, Number newValue) { | |
updateStyleClass(row); | |
} | |
}); | |
styledRowIndices.addListener(new ListChangeListener<Integer>() { | |
@Override | |
public void onChanged(Change<? extends Integer> change) { | |
updateStyleClass(row); | |
} | |
}); | |
return row; | |
} | |
/** | |
* | |
* @return The list of indices of the rows to which <code>styleClass</code> will be applied. | |
* Changes to the content of this list will result in the style class being immediately updated | |
* on rows whose indices are either added to or removed from this list. | |
*/ | |
public ObservableList<Integer> getStyledRowIndices() { | |
return styledRowIndices ; | |
} | |
private void updateStyleClass(TableRow<T> row) { | |
final ObservableList<String> rowStyleClasses = row.getStyleClass(); | |
if (styledRowIndices.contains(row.getIndex()) ) { | |
if (! rowStyleClasses.contains(styleClass)) { | |
rowStyleClasses.add(styleClass); | |
} | |
} else { | |
// remove all occurrences of styleClass: | |
rowStyleClasses.removeAll(Collections.singletonList(styleClass)); | |
} | |
} | |
} |
No, it doesn´t work if you sort the table ;-)
Thank you
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I don't think this works if you sort the table.