Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
JavaFX 8 version of https://gist.github.com/james-d/7813134, taking advantage of pseudoclasses.
@CHARSET "UTF-8";
.table-row-cell:highlighted {
-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.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
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 final TableView<Person> table = new TableView<>();
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<>("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<>("lastName"));
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory<>("email"));
table.setItems(data);
table.getColumns().addAll(Arrays.asList(firstNameCol, lastNameCol, emailCol));
final StyleChangingRowFactory<Person> rowFactory = new StyleChangingRowFactory<>("highlighted");
table.setRowFactory(rowFactory);
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
final Button highlightButton = new Button("Highlight");
highlightButton.disableProperty().bind(Bindings.isEmpty(table.getSelectionModel().getSelectedIndices()));
highlightButton.setOnAction(event -> rowFactory.getStyledRowIndices().setAll(table.getSelectionModel().getSelectedIndices()) );
final Button clearHighlightButton = new Button("Clear Highlights");
clearHighlightButton.disableProperty().bind(Bindings.isEmpty(rowFactory.getStyledRowIndices()));
clearHighlightButton.setOnAction(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 javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.css.PseudoClass;
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 pseudoclass state to true for 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 pseudoclass state of rows added to or removed
* from the list.
* The pseudoclass 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;("highlighted");
* 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 PseudoClass pseudoClass ;
private final ObservableList<Integer> styledRowIndices ;
private final Callback<TableView<T>, TableRow<T>> baseFactory ;
/**
* Construct a <code>StyleChangingRowFactory</code>,
* specifying the name of the pseudoclass that will be set
* for 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 pseudoclass that will be set for 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.pseudoClass = PseudoClass.getPseudoClass(styleClass);
this.baseFactory = baseFactory ;
this.styledRowIndices = FXCollections.observableArrayList();
}
/**
* Construct a <code>StyleChangingRowFactory</code>,
* which sets the pseudoclass <code>styleClass</code> to true for 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);
}
styledRowIndices.addListener((Change<? extends Integer> change)
-> row.pseudoClassStateChanged(pseudoClass, styledRowIndices.contains(row.getIndex()))
);
row.indexProperty().addListener((observable, oldValue, newValue)
-> row.pseudoClassStateChanged(pseudoClass, styledRowIndices.contains(row.getIndex()))
);
return row;
}
/**
*
* @return The list of indices of the rows for which the pseudoclass <code>styleClass</code> will be set.
* Changes to the content of this list will result in the pseudoclass state being immediately updated
* on rows whose indices are either added to or removed from this list.
*/
public ObservableList<Integer> getStyledRowIndices() {
return styledRowIndices ;
}
}
@SDen99
Copy link

SDen99 commented Jan 18, 2016

Nice example, but is there way to ensure that the highlighting moves with the row when the table is sorted?

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