我有一个 ReadOnlyBooleanProperty
名为 caution
对于每个 Trade
我创建的对象。每个Trade
对象填充 TableView 中的一行 fxTransactionLog
.现在我希望表格 cell 在 caution
时保持以绿色闪烁,并且同一卷单元格中的文本变为黄色。是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:我也尝试替换 OldItem
和 newItem
与 cell
并将其转换回 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/