【发布时间】:2020-07-11 16:51:30
【问题描述】:
我正在学习 JavaFX,现在正在研究 TableView。我想将股票价格放在表格中,并在更新时闪烁/闪烁单元格背景。
我进行了很多搜索,并看到了一些建议,但是无法找到我要查找的内容。此外,我似乎无法理解 TableView 的底层逻辑,特别是单元格的创建和更新时间和方式。
考虑下面的代码(大部分是从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 时我应该怎么做?
不知何故,我相信所有这些问题都是相关的,并且不知何故是由于我对细胞工厂和细胞创造的误解造成的。我在许多其他语言/框架中做过类似的事情,从来没有像现在这样困惑......
如果我能提供任何关于我做错了什么的信息,我将不胜感激!
【问题讨论】:
-
无论您做什么,您不得从 fx 应用程序线程更新场景图中的任何属性(就像您在 timertask 中所做的那样)
-
您无法控制何时/是否/如何重新使用单元格 - 因此在单元格内实现任何动画迟早都会中断。相反,在外部(在数据中或数据周围的包装器中)执行此操作,使表知道更改并让单元格根据状态自行更新。盐粒:并没有真正深入研究您的代码,只是看到了 Timertask 和动画;)
-
我认为 TableView 在 JavaFX 中是最先进的,可以做高级网格的东西(我在 JavaFX 中看到了很多高级动画演示 - 虽然不是在表格中),如果我理解正确地说,这些功能(在我看来真的很基本)是完全不可能使用 TableView 实现的?这让我大吃一惊!那么我应该朝哪个方向去获得闪烁更新的基本表呢?
-
@kleopatra 您提到了 timertask,但我只更新数据,而不是“场景图”中的任何内容(我真的不知道它实际上是什么)。否则我将如何模拟来自外部源的实时更新?
-
_使用TableView完全不可能实现_不,只是说单元格是不正确的地方来实现它。 但我只更新数据,而不是将“场景图”中的任何内容数据设置为在场景图中处于活动状态的表 - 所以实际上您确实更新了场景图(即 ui 中节点的层次结构)脱离 fx 应用程序线程。请做一些研究.. fx 具有广泛的并发支持(f.i. Task),使用它而不是普通的 java Timer/-Task