4

I have 2 sets of events that change the appearance of the table row.

  1. table flashing when some conditions flash
  2. table highlighting when a new item is added

Suppose my TableView is called table, my code structure is like this:

TableView<Trade> table = ... ;
table.setRowFactory(            private final BooleanBinding itemIsNewTrade = Bindings.isNotNull(itemProperty()).and(Bindings.equal(itemProperty(), recentlyAddedTrade));

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

            tv -> {
            TableRow<Trade> row = new TableRow<>();
            Timeline flasher = new Timeline(

                    new KeyFrame(Duration.seconds(0.5), e -> {
                        row.pseudoClassStateChanged(flashHighlight, true);
                    }),

                    new KeyFrame(Duration.seconds(1.0), e -> {
                        row.pseudoClassStateChanged(flashHighlight, false);
                    })
            );
            flasher.setCycleCount(Animation.INDEFINITE);

            ChangeListener<Boolean> cautionListener = (obs, cautionWasSet, cautionIsNowSet) -> {
                if (cautionIsNowSet) {
                    flasher.play();
                } else {
                    flasher.stop();
                    row.pseudoClassStateChanged(flashHighlight, false);
                }
            };

            row.itemProperty().addListener((obs, oldItem, newItem) -> {
                if (oldItem != null) {
                    oldItem.cautionProperty().removeListener(cautionListener);
                }
                if (newItem == null) {
                    flasher.stop();
                    row.pseudoClassStateChanged(flashHighlight, false);
                } else {
                    newItem.cautionProperty().addListener(cautionListener);
                    if (newItem.cautionProperty().get()) {
                        flasher.play();
                    } else {
                        flasher.stop();
                        row.pseudoClassStateChanged(flashHighlight, false);
                    }
                }
            });
            return row ;
        }
    );

UPDATE: The code for table flashing comes from here, while the code for table highlighting comes from here. I tried to combine both together by having a structure like above.

But I am getting error. I just do not know how to combine Anonymous inner class and lambda expression together

How should I overcome this ?

Community
  • 1
  • 1
mynameisJEFF
  • 4,073
  • 9
  • 50
  • 96

1 Answers1

4

The best way to do this, is to create a separate TableRow class instead of an anonymous inner class.

I created an example implementation below.

Some things worth mentioning:

  1. You need do define the style .table-row-cell:new in your CSS file for the newly added TableRow.
  2. You need to define the style .table-row-cell:flash in your CSS file for the TableRow(s) which are currently flashing.
  3. The constructor needs to know (ObjectExpression) where the recently added item is stored, which is probably recentlyAddedTrade in your case.
  4. The constructor needs to know (Function<T, BooleanExpression>) how to extract the property determining the flashing state. In your case it should be Trade::cautionProperty.

Here is the example implementation:

public static class AnimatedTableRow<T> extends TableRow<T> {

    private static final PseudoClass PS_NEW = PseudoClass.getPseudoClass("new");
    private static final PseudoClass PS_FLASH = PseudoClass.getPseudoClass("flash");

    private final ObjectExpression<T> recentItem;
    private final InvalidationListener recentlyAddedListener = fObs -> recentItemChanged();

    private final Function<T, BooleanExpression> flashExtractor;
    private final ChangeListener<Boolean> flashListener = (fObs, fOld, fNew) -> flasherChanged(fNew);
    private final Timeline flashTimeline;

    public AnimatedTableRow(ObjectExpression<T> fRecentlyAddedProperty,
            Function<T, BooleanExpression> fFlashExtractor) {
        recentItem = fRecentlyAddedProperty;
        recentItem.addListener(new WeakInvalidationListener(recentlyAddedListener));

        flashExtractor = fFlashExtractor;
        flashTimeline = new Timeline(
                new KeyFrame(Duration.seconds(0.5), e -> pseudoClassStateChanged(PS_FLASH, true)),
                new KeyFrame(Duration.seconds(1.0), e -> pseudoClassStateChanged(PS_FLASH, false)));
        flashTimeline.setCycleCount(Animation.INDEFINITE);
    }

    private void flasherChanged(boolean fNew) {
        if (fNew) {
            flashTimeline.play();
        } else {
            flashTimeline.stop();
            pseudoClassStateChanged(PS_FLASH, false);
        }
    }

    private void recentItemChanged() {
        final T tmpRecentItem = recentItem.get();
        pseudoClassStateChanged(PS_NEW, tmpRecentItem != null && tmpRecentItem == getItem());
    }

    @Override
    protected void updateItem(T item, boolean empty) {
        if (getItem() != null) {
            final BooleanExpression be = flashExtractor.apply(getItem());
            if (be != null) {
                be.removeListener(flashListener);
            }
        }

        super.updateItem(item, empty);

        if (getItem() != null) {
            final BooleanExpression be = flashExtractor.apply(getItem());
            if (be != null) {
                be.addListener(flashListener);
                flasherChanged(be.get());
            }
        }

        recentItemChanged();
    }
}

Usage:

table.setRowFactory(tableView -> new AnimatedTableRow<>(recentlyAddedPerson, Person::flashProperty));
eckig
  • 10,964
  • 4
  • 38
  • 52