javafx - 表视图中的行为重复和反向行(javafx)
问题描述
在 tableview 的更新中,我已将代码简化为以下内容。
由于某些原因,我没有像预期的那样使用 JavaFx 的属性,也许这种行为与此有关。来自 JTable 和观察者模式,我想尝试如何在 javafx 中实现这一点。但是,我的表格数据都很好,但是当我尝试更改背景颜色时,表格范围之外会出现更多行。我检查了 Observable 列表的大小,它符合预期。
我无法真正看到或理解 datas.set 是如何产生这种行为的,这是我的代码遗漏的东西,还是 Javafx tableview 的预期行为。
基本上使用的逻辑是我从另一个类(我在这里用线程模拟)接收更新,然后我将用更新的数据更新可观察列表的相应行。
谢谢。
package application;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
Pane root = new Pane();
root.getChildren().add(new TableModel().getTable());
Scene scene = new Scene(root,400,800);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
package application;
import java.util.Random;
import javafx.application.Platform;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.util.Callback;
public class TableModel{
private TableView<TableData> table = new TableView<>();
private ObservableList<TableData> datas;
@SuppressWarnings("unchecked")
public TableModel() {
datas = FXCollections.observableArrayList();
final TableColumn<TableData, Integer> cId = new TableColumn<>("ID");
cId.setCellValueFactory(
new Callback<TableColumn.CellDataFeatures<TableData, Integer>, ObservableValue<Integer>>() {
@Override
public ObservableValue<Integer> call(CellDataFeatures<TableData, Integer> param) {
return new SimpleObjectProperty<Integer>(param.getValue().getId());
}
});
cId.setCellFactory(e -> new TableCell<TableData, Integer>() {
@Override
public void updateItem(Integer item, boolean empty) {
// Always invoke super constructor.
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(String.valueOf(item));
if (getIndex() == 0) {
this.setStyle("-fx-background-color: green;");
}else {
this.setStyle("-fx-background-color: red;");
}
}
}
});
table.getColumns().addAll(cId);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.setItems(datas);
table.setPrefSize(150, 450);
datas.add(0, new TableData(0));
datas.add(1, new TableData(0));
datas.add(2, new TableData(0));
datas.add(3, new TableData(0));
datas.add(4, new TableData(0));
new Thread(()->{
while(true) {
Random rand = new Random(10);
update(rand.nextInt());
try {
Thread.sleep(3000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}).start(); ;
}
public TableView<TableData> getTable() {
return table;
}
public void update(int num) {
TableData data = new TableData(num);
Platform.runLater(() -> {
datas.set(0, data);
});
}
private class TableData {
private SimpleIntegerProperty cId = new SimpleIntegerProperty();
public TableData(Integer conid) {
this.cId.setValue(conid);
}
public Integer getId() {
return cId.get();
}
}
}
输出与datas.set(0, data);
输出无datas.set(0, data);
解决方案
您的代码有 4 个问题:
第一期
final TableColumn<TableData, Integer> cId = new TableColumn<>("ID");
cId.setCellValueFactory(
new Callback<TableColumn.CellDataFeatures<TableData, Integer>, ObservableValue<Integer>>() {
@Override
public ObservableValue<Integer> call(CellDataFeatures<TableData, Integer> param) {
return new SimpleObjectProperty<Integer>(param.getValue().getId());
}
});
这可以防止属性的更改自动触发更新。例如
table.getItems().get(0).cId.set(5);
不会导致表的更新。要解决此问题,请返回属性本身,而不是在执行时使用属性值初始化的新创建的属性cellValueFactory
。
final TableColumn<TableData, Number> cId = new TableColumn<>("ID");
cId.setCellValueFactory(
new Callback<TableColumn.CellDataFeatures<TableData, Number>, ObservableValue<Number>>() {
@Override
public ObservableValue<Number> call(TableColumn.CellDataFeatures<TableData, Number> param) {
return param.getValue().cId;
}
});
第 2 期
@Override
public void updateItem(Integer item, boolean empty) {
// Always invoke super constructor.
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(String.valueOf(item));
if (getIndex() == 0) {
this.setStyle("-fx-background-color: green;");
}else {
this.setStyle("-fx-background-color: red;");
}
}
}
考虑到单元格在填充了值后可能会变为空,因此此实现是不正确的。您应该确保在单元格变空的情况下恢复“空”外观。这需要您清除样式属性:
@Override
protected void updateItem(Number item, boolean empty) {
// Always invoke supertype's implementation
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setStyle(null);
} else {
setText(item.toString());
if (getIndex() == 0) {
this.setStyle("-fx-background-color: green;");
} else {
this.setStyle("-fx-background-color: red;");
}
}
}
问题 3 和 4
new Thread(()->{
while(true) {
Random rand = new Random(10);
update(rand.nextInt());
try {
Thread.sleep(3000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}).start();
即使关闭 GUI 以防止程序终止,该线程仍将保持活动状态。您可以使用守护线程轻松解决此问题。
此外Random
,用种子初始化会返回确定性结果。由于您Random
在循环的每次迭代中都使用相同的种子重新初始化,因此rand.nextInt()
返回的值始终相同。您需要移动Random
循环外部的初始化。
Thread thread = new Thread(() -> {
Random rand = new Random(10);
while (true) {
update(rand.nextInt());
try {
Thread.sleep(3000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
});
thread.setDaemon(true);
thread.start();
推荐阅读
- c# - NUnit 测试适配器发现测试:是否可以忽略项目?
- python - 如何搜索关键字并打印文件,仅打印关键字中的行
- linux - 如何将小于一页大小(4Kb)的Linux内核的特定物理地址范围映射到用户空间
- javascript - 发出在循环中写入文件并从临时目录中读取的问题
- javascript - 在一组字符之前匹配一些东西,直到正则表达式中的一个字符
- c# - 从 SFTP 下载文件的问题?(未处理的异常)
- grep - Show a match (grep) and show one column of the matched line
- node.js - 由于“Module not found: Error: Can't resolve”错误,Webpack 突然无法编译
- python - Jupyter 打印语句问题
- cocoa-touch - ARKit 人脸追踪 PBR