javafx:如何创建一个 cellFactory 来考虑一个对象的两个属性?

标签 java javafx

我有一个 ReadOnlyBooleanProperty名为 caution对于每个 Trade我创建的对象。每个Trade对象填充 TableView 中的一行 fxTransactionLog .现在我希望表格 cellcaution 时保持以绿色闪烁,并且同一卷单元格中的文本变为黄色。是true .

现在我正在努力让电池闪光。问题是我要求体积属性列可编辑并根据另一个 boolean 绑定(bind)属性闪烁。

本质上,cellFactory 正在查看两个不同的属性,这就是使这个问题变得非常困难的原因。 因为我经常要从volume列中查找表格行来获取Trade对象,然后查找相应的boolean-binding caution属性。这种方法似乎很复杂。

public class Trade{
       private DoubleProperty volume;
       private ReadOnlyBooleanWrapper caution;

       public Trade(double volume){
            this.volume = new SimpleDoubleProperty(volume);
            this.caution = new ReadOnlyBooleanWrapper();
            this.caution.bind(this.volume.greaterThan(0));
       }

}

这是我的代码,但它给我错误。

fvolume.setCellValueFactory(new PropertyValueFactory<Trade,Double>("price"));
PseudoClass flashHighlight = PseudoClass.getPseudoClass("flash-highlight");
        volume.setCellFactory(


            tv -> {
                TableCell<Trade, Double> cell = new TableCell<>();
                Timeline flasher = new Timeline(

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

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

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

                cell.itemProperty().addListener((obs, oldItem, newItem) -> {
                    if (oldItem != null) {
                        oldItem.cautionProperty().removeListener(cautionListener); //<--- PROBLEM 1
                    }
                    if (newItem == null) {
                        flasher.stop();
                        cell.pseudoClassStateChanged(flashHighlight, false);
                    } else {
                        newItem.cautionProperty().addListener(cautionListener); //<-- PROBLEM 2
                        if (newItem.cautionProperty().get()) { //<-- PROBLEM 3
                            flasher.play();
                        } else {
                            flasher.stop();
                            cell.pseudoClassStateChanged(flashHighlight, false);
                        }
                    }
                });
                return cell ;
            }
        );

更新:我突出显示了代码出错的地方,请参阅 PROBLEM 1,2,3多于。出现错误:the method cautionProperty() is undefined for the Type Double.

我还尝试添加 getTableRow().getItem()oldItem.cautionProperty().removeListener(cautionListener); <--- PROBLEM 1 , 变成 oldItem.getTableRow().getItem().cautionProperty().removeListener(cautionListener); .

我仍然遇到同样的错误:the method cautionProperty() is undefined for the Type Double.

UPDATE2:我也尝试替换 OldItemnewItemcell并将其转换回 Trade对象,但是当我编译时,我在以下几行中得到了一个 NullPointerException。

((Trade)cell.getTableRow().getItem()).cautionProperty().removeListener(cautionListener);


((Trade) cell.getTableRow().getItem()).cautionProperty().addListener(cautionListener);

错误日志:

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
    at application.Controller.lambda$19(Controller.java:378)
    at application.Controller$$Lambda$370/220504298.changed(Unknown Source)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)

更新 3: 我尝试了另一种方法来解决这个问题。但是我仍然遇到类似的错误:NullPointerException在以下行中

 final BooleanExpression be = flashExtractor.apply((T) getTableRow().getItem());

方法二:

public static class AnimatedTableCell<T> extends TableCell<T,Double>{
    private static final PseudoClass PS_Cell_Flash = PseudoClass.getPseudoClass("flash-cell-positive");

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

    // constructor
    public AnimatedTableCell(Function<T, BooleanExpression> fFlashExtractor){
        flashExtractor = fFlashExtractor;
        flashTimeline = new Timeline(
                new KeyFrame(Duration.seconds(0.5), e -> pseudoClassStateChanged(PS_Cell_Flash, true)),
                new KeyFrame(Duration.seconds(1.0), e -> pseudoClassStateChanged(PS_Cell_Flash, false)));
        flashTimeline.setCycleCount(Animation.INDEFINITE);
    }

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

    //@SuppressWarnings("unchecked")
    @Override
    protected void updateItem(Double item, boolean empty) {
        System.out.println("getItem(): " + (T) getTableRow().getItem());
        if (getItem() != null) {
            // Problem IN THIS LINE
            final BooleanExpression be = flashExtractor.apply((T) getTableRow().getItem());
            if (be != null) {
                be.removeListener(flashListener);
            }
        }

        super.updateItem(item, empty);

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

}

最佳答案

这是@eckig 解决方案的替代方案(非常好:这只是一种不同的方法)。

由于您基本上只是尝试更改样式,因此您可以在 TableRow 上设置 CSS 伪类。而不是 TableCell ,然后只需使用您的 CSS 样式表来更新您感兴趣的特定单元格的颜色。

这里的优势,也许是对于 TableRow 来说要容易得多。查看 Trade 中的任何属性比 TableCell 的类,还有你的 TableCell仍然是更直观的类型 ( TableCell<Trade, Double> ),其数据是显示的数据。

所以你会这样做

fxTransactionLog.setRowFactory(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);
        }
    };

    cell.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()) { //<-- PROBLEM 3
                flasher.play();
            } else {
                flasher.stop();
                row.pseudoClassStateChanged(flashHighlight, false);
            }
        }
    });
    return row ;
});

现在你只需要给你想要对状态变化使用react的单元格一个样式类。您可以使用细胞工厂执行此操作:

fxTransactionLogVolume.setCellFactory(col -> {
    TableCell<Trade, Double> cell = new TableCell<Trade, Double>() {
        @Override
        public void updateItem(Double volume, boolean empty) {
            super.updateItem(volume, empty);
            if (empty) {
                setText(null);
            } else {
                setText(volume.toString());
            }
        }
    };

    cell.getStyleClass().add("trade-volume-cell");
    return cell ;
}

现在在你的外部样式表中你可以做

.table-row-cell:flash-highlight .trade-volume-cell {
    -fx-background-color: transparent, green ;
    -fx-text-fill: yellow ;
}

请注意,您可以在此处使用您喜欢的任何单元实现;由于“闪烁”实际上是由表行控制的,因此不会造成干扰。所以如果你有一些更复杂的单元实现(例如编辑),你仍然可以使用它:

fxTransactionLogVolume.setCellFactory( col -> {
    MySpecialTableCell cell = new MySpecialTableCell();
    cell.getStyleClass().add("trade-volume-cell");
    return cell ;
});

public class MySpecialTableCell extends TableCell<Trade, Double> {

    // whatever you need to do here...

}

关于javafx:如何创建一个 cellFactory 来考虑一个对象的两个属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32273925/

相关文章:

java - Android 错误/点击崩溃

java - 将矩形绘制到屏幕后如何清除它?

java - 如何从 TableView 中删除大量选定的行?

在循环内调用appendText()时JavaFx TextArea卡住

java - Spark : Ignoring or handling DataSet select errors

Java多级菜单结构

java - 确定等距游戏和不规则形状的绘制顺序 (Java)

java - 上下文菜单仅显示几个 TableView 行 javafx fxml

java - 确定fx :id of a pressed button

JavaFX TextField 文本验证