Skip to content

Instantly share code, notes, and snippets.

@james-d
Last active May 22, 2021 14:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save james-d/7813134 to your computer and use it in GitHub Desktop.
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.
@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;
}
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 ;
}
}
}
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&lt;Integer&gt;</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&lt;MyDomainObject&gt; table = ... ;
* final StyleChangingRowFactory&lt;MyDomainObject&gt; rowFactory
* = new StyleChangingRowFactory&lt;&gt;("highlightedRow");
* table.setRowFactory(rowFactory);
*
* Button highlightButton = new Button("Highlight selected rows");
* highlightButton.setOnAction(new EventHandler&lt;ActionEvent&gt;() {
* &#0064;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&lt;T&gt;</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));
}
}
}
@dbh21
Copy link

dbh21 commented Aug 4, 2015

I don't think this works if you sort the table.

@Pitority
Copy link

No, it doesn´t work if you sort the table ;-)

Copy link

ghost commented Aug 15, 2020

Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment