无法在更新时使带有闪烁单元格背景的 JavaFX 表格视图正常工作



我正在学习JavaFX,现在正在研究TableView。我想把股票价格放在一张表中,并在更新时闪烁单元格背景。

我搜索了很多,看到了一些建议,但没能找到我想要的。此外,我似乎很难理解TableViews的底层逻辑,特别是何时以及如何创建和更新单元格。

考虑以下代码(主要是从http://jaakkola.net/juhani/blog/?p=233):

public class FlashingTableCell<S, T> extends TableCell<S, T> {
private static final Color INCREASE_HIGHLIGHT_COLOR = Color.rgb(0, 255, 0, 0.8);
private static final Color DECREASE_HIGHLIGHT_COLOR = Color.rgb(255, 0, 0, 0.8);
private static final Color HIGHLIGHT_COLOR = Color.rgb(0, 255, 0, 0.8);
private static final Duration HIGHLIGHT_TIME = Duration.millis(600);
private final Background bgIncrease = new Background(new BackgroundFill(INCREASE_HIGHLIGHT_COLOR, CornerRadii.EMPTY, Insets.EMPTY));
private final Background bgDecrease = new Background(new BackgroundFill(DECREASE_HIGHLIGHT_COLOR, CornerRadii.EMPTY, Insets.EMPTY));
private final Background bgChange = new Background(new BackgroundFill(HIGHLIGHT_COLOR, CornerRadii.EMPTY, Insets.EMPTY));
private final BorderPane background = new BorderPane();
private final Label lblText = new Label("");
private final FadeTransition animation = new FadeTransition(HIGHLIGHT_TIME, background);
private final StackPane container = new StackPane();
private T prevValue;
private S prevItem;
final private Comparator<T> comparator;

public FlashingTableCell(Comparator<T> comparator, Pos alignment) {
super();
this.comparator = comparator;
lblText.textProperty().bindBidirectional(textProperty());
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setPadding(Insets.EMPTY);
container.getChildren().addAll(background, lblText);
container.setAlignment(alignment);
setGraphic(container);
}
@Override
protected void updateItem(T value, boolean empty) {
super.updateItem(value, empty);
System.out.println("updateItem " + this.hashCode() + " " + getIndex() + " value=" + value + " (" + prevValue + ")" + empty);
S currentItem = getTableRow() != null && getTableRow().getItem() != null ? (S) getTableRow().getItem() : null;
/*
* We check that the value has been updated and that the row model/item
* under the cell is the same. JavaFX table reuses cells so item is not
* always the same!
*/
boolean valueChanged = (prevValue == null && value != null) || (value != null && (prevValue.hashCode() != value.hashCode()));
boolean sameItem = currentItem != null && prevItem != null && currentItem == prevItem;
if (valueChanged && sameItem) {
if (comparator != null) {
int compare = comparator.compare(value, prevValue);
if (compare > 0) {
background.setBackground(bgIncrease);
} else if (compare < 0) {
background.setBackground(bgDecrease);
}
} else {
background.setBackground(bgChange);
}
lblText.setText(String.format("%1.2f", value));
animation.setFromValue(1);
animation.setToValue(0);
animation.setCycleCount(1);
animation.setAutoReverse(false);
animation.playFromStart();
}
prevValue = value;
prevItem = currentItem;
}
}

这个细胞工厂:

public class FlashingTableCellFactory<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> {
@Override
public TableCell<S, T> call(TableColumn<S, T> p) {
System.out.println("************** CREATING FLASHING TABLE CELL **************");
FlashingTableCell<S,T> cell = new FlashingTableCell<S,T>(null, Pos.CENTER);
return cell;
}
}

我是这样使用的:

public class Main extends Application {
Timer timer = new Timer();
@Override
public void start(Stage primaryStage) {
TableView<InstrumentPrice> table = new TableView<>();
ObservableList<InstrumentPrice> data = getInitialTableData();
table.setItems(data);
TableColumn<InstrumentPrice, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(new PropertyValueFactory<>("instrumentName"));
TableColumn<InstrumentPrice, Double> openCol = new TableColumn<>("Open");
openCol.setCellValueFactory(new PropertyValueFactory("open"));
// Flashing table cell
FlashingTableCellFactory<tabletest1.InstrumentPrice, Double> ftc2 = new FlashingTableCellFactory<tabletest1.InstrumentPrice, Double>();
openCol.setCellFactory(ftc2);
table.getColumns().setAll(nameCol, openCol);
timer.schedule(new TimerTask() {
@Override
public void run() {
// Get and update first item in data array
InstrumentPrice p = data.get(0);
p.setOpen(p.getOpen()+1.0);
data.set(0, p);
}
}, 10*1000, 5*1000); // 10 seconds
StackPane rootLayout = new StackPane(table);
Scene scene = new Scene(rootLayout, 1000, 300);
primaryStage.setTitle("Example");
primaryStage.setScene(scene);
primaryStage.setX(0);
primaryStage.setY(0);
primaryStage.show();
}
private ObservableList<InstrumentPrice> getInitialTableData() {
List list = new ArrayList<InstrumentPrice>();
list.add(new InstrumentPrice("ABC", 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0.0, 0, 0.0, 0));
list.add(new InstrumentPrice("DEF", 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0.0, 0, 0.0, 0));
list.add(new InstrumentPrice("GHI", 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0.0, 0, 0.0, 0));
list.add(new InstrumentPrice("JKL", 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0.0, 0, 0.0, 0));
list.add(new InstrumentPrice("MNO", 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0.0, 0, 0.0, 0));
list.add(new InstrumentPrice("PQR", 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0.0, 0, 0.0, 0));
list.add(new InstrumentPrice("STU", 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0.0, 0, 0.0, 0));
list.add(new InstrumentPrice("VWX", 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0.0, 0, 0.0, 0));
ObservableList<InstrumentPrice> data = FXCollections.observableList(list);
return data;
}

public static void main(String[] args) {
launch(args);
}
}

我把一些调试打印到控制台上,下面是一些:

************** CREATING FLASHING TABLE CELL 1299017468
updateItem 1299017468 0 value=0.0 (null)false
updateItem 1299017468 1 value=0.0 (0.0)false
updateItem 1299017468 2 value=0.0 (0.0)false
updateItem 1299017468 3 value=0.0 (0.0)false
updateItem 1299017468 4 value=0.0 (0.0)false
updateItem 1299017468 5 value=0.0 (0.0)false
updateItem 1299017468 6 value=0.0 (0.0)false
updateItem 1299017468 7 value=0.0 (0.0)false
updateItem 1299017468 -1 value=null (0.0)true
************** CREATING FLASHING TABLE CELL 312035237
updateItem 312035237 0 value=0.0 (null)false
updateItem 312035237 -1 value=null (0.0)true
************** CREATING FLASHING TABLE CELL 616040193
updateItem 616040193 0 value=0.0 (null)false
************** CREATING FLASHING TABLE CELL 1836880566
updateItem 1836880566 1 value=0.0 (null)false
************** CREATING FLASHING TABLE CELL 1717614984
updateItem 1717614984 2 value=0.0 (null)false
************** CREATING FLASHING TABLE CELL 114981818
updateItem 114981818 3 value=0.0 (null)false
************** CREATING FLASHING TABLE CELL 151715918
updateItem 151715918 4 value=0.0 (null)false
************** CREATING FLASHING TABLE CELL 1690114806
updateItem 1690114806 5 value=0.0 (null)false
************** CREATING FLASHING TABLE CELL 397552694
updateItem 397552694 6 value=0.0 (null)false
************** CREATING FLASHING TABLE CELL 705642570
updateItem 705642570 7 value=0.0 (null)false
************** CREATING FLASHING TABLE CELL 575848070
updateItem 575848070 8 value=null (null)true
************** CREATING FLASHING TABLE CELL 349657094
updateItem 349657094 9 value=null (null)true
************** CREATING FLASHING TABLE CELL 221445895
updateItem 221445895 10 value=null (null)true
************** CREATING FLASHING TABLE CELL 666967962
updateItem 666967962 11 value=null (null)true
updateItem 616040193 -1 value=null (0.0)true
updateItem 1836880566 -1 value=null (0.0)true
updateItem 1717614984 -1 value=null (0.0)true
updateItem 114981818 -1 value=null (0.0)true
updateItem 151715918 -1 value=null (0.0)true
updateItem 1690114806 -1 value=null (0.0)true
updateItem 397552694 -1 value=null (0.0)true
updateItem 705642570 -1 value=null (0.0)true
updateItem 666967962 0 value=1.0 (null)false
updateItem 666967962 0 value=1.0 (1.0)false
updateItem 221445895 1 value=0.0 (null)false
updateItem 221445895 1 value=0.0 (0.0)false
updateItem 349657094 2 value=0.0 (null)false
updateItem 349657094 2 value=0.0 (0.0)false
updateItem 575848070 3 value=0.0 (null)false
updateItem 575848070 3 value=0.0 (0.0)false
updateItem 705642570 4 value=0.0 (0.0)false
updateItem 705642570 4 value=0.0 (0.0)false
updateItem 397552694 5 value=0.0 (0.0)false
updateItem 397552694 5 value=0.0 (0.0)false
updateItem 1690114806 6 value=0.0 (0.0)false
updateItem 1690114806 6 value=0.0 (0.0)false
updateItem 151715918 7 value=0.0 (0.0)false
updateItem 151715918 7 value=0.0 (0.0)false
updateItem 114981818 8 value=null (0.0)true
updateItem 114981818 8 value=null (0.0)true
updateItem 1717614984 9 value=null (0.0)true
updateItem 1717614984 9 value=null (0.0)true
updateItem 1836880566 10 value=null (0.0)true
updateItem 1836880566 10 value=null (0.0)true
updateItem 616040193 11 value=null (0.0)true
updateItem 616040193 11 value=null (0.0)true

有很多问题,但主要是我想知道:

  • 为什么每次更新都调用updateItem((方法两次
  • 当我只更新第0行时,为什么我会得到第1..n行的updateItem((调用?我没有调整桌子的大小或做任何事情
  • "sameItem"逻辑似乎根本不起作用
  • 当调用updateItem时使用empty==true时,我该怎么办

不知何故,我相信所有这些问题都是相关的,而且不知何故是由于我对细胞工厂和细胞创造的误解造成的。我在许多其他语言/框架中也做过类似的事情,从未像现在这样困惑。。。

如果有任何关于我做错了什么的信息,我将不胜感激!

您的细胞工厂必须是这样的。

sizeBuy.setCellFactory(new Callback<TableColumn<buyOrderBook, Double>, TableCell<buyOrderBook, Double>>() {
public TableCell<buyOrderBook, Double> call(TableColumn<buyOrderBook, Double> column) {
Comparator<Double> comparator = new Comparator<Double>() {
@Override
public int compare(Double o1, Double o2) {
return o1.compareTo(o2);
}
};

FlashingTableCell cell = new FlashingTableCell(comparator, Pos.CENTER_RIGHT);
return label;
}
});

sizeBuy是我的列名。buyOrderBook是该表视图的类,Double是该列的值类型。因为我现在正在开发这种类型的应用程序,所以我只是改进了FlashingTableCell类,并使其做得正确,这是FlashingTableCell类的完整代码:

class FlashingTableCell<S, T> extends TableCell<S, T> {
private static final Color INCREASE_HIGHLIGHT_COLOR = Color.rgb(0, 255, 0, 0.4);
private static final Color DECREASE_HIGHLIGHT_COLOR = Color.rgb(255, 0, 0, 0.4);
private static final Color NOTCHANGE_COLOR = Color.rgb(0, 0, 0, 0.0);
private static final Color HIGHLIGHT_COLOR = Color.rgb(255, 255, 0, 0.7);
private static final Duration HIGHLIGHT_TIME = Duration.millis(500);
private final Background bgIncrease = new Background(new BackgroundFill(INCREASE_HIGHLIGHT_COLOR, CornerRadii.EMPTY, Insets.EMPTY));
private final Background bgDecrease = new Background(new BackgroundFill(DECREASE_HIGHLIGHT_COLOR, CornerRadii.EMPTY, Insets.EMPTY));
private final Background bgNotChange = new Background(new BackgroundFill(NOTCHANGE_COLOR, CornerRadii.EMPTY, Insets.EMPTY));
private final Background bgChange = new Background(new BackgroundFill(HIGHLIGHT_COLOR, CornerRadii.EMPTY, Insets.EMPTY));
private final BorderPane background = new BorderPane();
private final LabeledText lblText = new LabeledText(this);
private final FadeTransition animation = new FadeTransition(HIGHLIGHT_TIME, background);
private final StackPane container = new StackPane();
private T prevValue;
private S prevItem;
final private Comparator<T> comparator;

public FlashingTableCell(Comparator<T> comparator, Pos alignment) {
super();
this.comparator = comparator;
lblText.textProperty().bindBidirectional(textProperty());
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
lblText.setStyle("-fx-text-fill: lightgray ;");
setPadding(new Insets(0,10,0,0));
container.getChildren().addAll(background, lblText);
container.setAlignment(alignment);
setGraphic(container);
animation.setFromValue(1.0);
animation.setToValue(0);
// animation.setCycleCount(Timeline.INDEFINITE);
animation.setCycleCount(0);
animation.setAutoReverse(false);

itemProperty().addListener((obs, oldItem, newItem) -> {

if (newItem != null && oldItem != null && getIndex() >= 0  ) {
int compare = comparator.compare(newItem , oldItem);               
if (compare > 0) {
background.setBackground(bgIncrease);
animation.playFromStart();
} else if (compare < 0) {
background.setBackground(bgDecrease);
animation.playFromStart();
}

}
});
}
@Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if(!isEmpty()) {
lblText.setText(String.format("%1.2f", item));
}
}

}

我正在使用交易所的websocket进行实时数据处理。最佳实践您也需要使用实时数据,因为就像我现在的项目一样,我为订单簿实现了这种方法。订单有时会在200毫秒以下发生变化,所以我们可以看看这种方法是否有很大的延迟。

这里是我的项目SS:javafx订单簿的闪烁实现

最新更新