Java FX 8 table row highlighting

2019-06-09 08:35发布

问题:

I have a Java FX8 Table, I have populated it using an observable list. The table is displaying the populated data.

During user interaction a new row is created, I add this new data to the observable list. The table gets refreshed.

I know how to move focus as well as to scroll to this newly added row. However I want this row to be highlighted to show that it is newly added.

How do I get a reference to the whole row as a Node, so that I can use this node value to highlight the row.

回答1:

Use a rowFactory on the TableView which creates a TableRow. That TableRow is the Node representing the whole row. The slightly tricky part is identifying when the row represents a "recently added row".

I would approach this as follows. I'll use the usual contact table example

  1. Define an ObjectProperty<Person> to represent the recently added person. Most of the time this will be null but when a new Person is added to the list, it will be set to that new Person:

final ObjectProperty<Person> recentlyAddedPerson = new SimpleObjectProperty<>();

  1. Register a ListListener with the table's items list. When a new item is added to the list, update the recentlyAddedPerson. Since you don't want the new person to be labeled as "new" indefinitely, start a pause transition that will reset recentlyAddedPerson to null after some delay (a second or two).:

    final Duration timeToGetOld = Duration.seconds(1.0);
    
    table.getItems().addListener((Change<? extends Person> change) -> {
        while (change.next()) {
            if (change.wasAdded()) {
                List<? extends Person> addedPeople = change.getAddedSubList();
                Person lastAddedPerson = addedPeople.get(addedPeople.size()-1);
                recentlyAddedPerson.set(lastAddedPerson);
    
                // set back to null after a short delay, unless changed since then:
                PauseTransition agingTime = new PauseTransition(timeToGetOld);
                agingTime.setOnFinished(event -> {
                    if (recentlyAddedPerson.get() == lastAddedPerson) {
                        recentlyAddedPerson.set(null);
                    }
                });
                agingTime.play();
            }
        }
    });
    
  2. Create a row factory for the table. This row factory returns a custom TableRow. This custom TableRow creates a BooleanBinding which is set to true if this row represents a recently-added row. (This will be true if the row's item is not null and is equal to the recentlyAddedPerson defined above.)

  3. To actually implement the highlighting, I would just use a CSS PseudoClass. The implementation of the highlighting then becomes trivial; just set the pseudoclass state to the value in the BooleanBinding defined in the TableRow implementation.

So you'll need something like:

    final PseudoClass newPersonPseudoClass = PseudoClass.getPseudoClass("new");

and the rowFactory looks like

    table.setRowFactory(tableView -> new TableRow<Person>() {
        // Bindings API uses weak listeners, so this needs to be a field to
        // make sure it stays in scope as long as the TableRow is in scope.
        private final BooleanBinding itemIsNewPerson 
            = Bindings.isNotNull(itemProperty())
            .and(Bindings.equal(itemProperty(), recentlyAddedPerson));

        {
            // anonymous constructor:
            itemIsNewPerson.addListener((obs, wasNew, isNew)
                    -> pseudoClassStateChanged(newPersonPseudoClass, isNew));
        }
    });

Finally, just add the following css to your stylesheet:

.table-row-cell:new {
    -fx-background-color: darkseagreen ;
}

I posted the complete example as a gist.



标签: javafx-8