如何在 JavaFX 中将嵌套的任务进度属性绑定到表视图?



Enironment:OpenJDK12, JavaFX 11

上下文:我正在尝试向 TableView 显示任务进度,为此,当我的代码不太复杂时,我的 Task 对象包含 Bean 属性,而 TableView 数据模型是我的 Task 对象。

public class MyTask extends Task<Void>{
private String name;
//other properties
public Void call() {
//"progress" property is inherited from Task.
//do something and updateProgress()
}
}
public class MyController {
...
@FXML
private TableView<MyTask> dataTable;
@FXML
private TableColumn<MyTask,Double> progressCol;
...
progressCol.setCellValueFactory(new PropertyValueFactory<MyTask, Double>("progress"));
progressCol.setCellFactory(ProgressCell.<Double>forTableColumn());
...
}

效果很好。但是我想将 Task 与 Bean 属性分开,所以我决定制作一种包装器,但我无法再检索进度属性。

编辑

示例代码:

我的应用

public class MyApp extends Application {
@Override
public void start(Stage stage) throws IOException {
stage.setMinWidth(800);
stage.setMinHeight(500);
FXMLLoader sceneLoader = new FXMLLoader(MyApp.class.getResource("MyScene.fxml"));
Parent parent = sceneLoader.load();
Scene scene = new Scene(parent);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}

我的控制器

public class MyController implements Initializable{
@FXML
private TableView<MyWrapper> dataTable;
@FXML
private TableColumn<MyWrapper, String> nameColumn;
@FXML
private TableColumn<MyWrapper, Double> progressColumn;
public MyController() {
}
@Override
public void initialize(URL location, ResourceBundle resources) {        
nameColumn.setCellValueFactory((TableColumn.CellDataFeatures<MyWrapper, String> download) -> download.getValue()
.getMyBean().nameProperty());
//This line only works when MyWrapper has progressPropery() method
//progressColumn.setCellValueFactory(new PropertyValueFactory<>("progress"));
progressColumn.setCellFactory(ProgressCell.<Double>forTableColumn());
MyWrapper w1 = new MyWrapper("qqqqqqq");
MyWrapper w2 = new MyWrapper("wwwwww");
MyWrapper w3 = new MyWrapper("eeeeeee");
ObservableList<MyWrapper> obsList = FXCollections.observableArrayList();
obsList.addAll(w1,w2,w3);
dataTable.setItems(obsList);
Thread t1 = new Thread(w1.getMyTask());
t1.start();

}

我的包装器

public class MyWrapper {
private SimpleObjectProperty<MyBean> myBean;
private SimpleObjectProperty<MyTask> myTask;
public MyWrapper(String name) {
myBean = new SimpleObjectProperty<MyBean>();
myBean.setValue(new MyBean());
myBean.getValue().setName(name);
myTask = new SimpleObjectProperty<MyTask>();
myTask.setValue(new MyTask());
}
public MyBean getMyBean() {
return myBean.getValue();
}
public MyTask getMyTask() {
return myTask.getValue();
}
}

米豆

public class MyBean {
private SimpleStringProperty name;
public MyBean() {
name = new SimpleStringProperty("--");
}
public SimpleStringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.setValue(name);
}
}

我的任务

public class MyTask extends Task<Void>{
@Override
protected Void call() throws Exception {
// Set the total number of steps in our process
double steps = 1000;
// Simulate a long running task
for (int i = 0; i < steps; i++) {
Thread.sleep(10); // Pause briefly
// Update our progress and message properties
updateProgress(i, steps);
updateMessage(String.valueOf(i));
}       return null;
}
}

进度单元格

public class ProgressCell extends TableCell<MyWrapper, Double> {
private ProgressBar bar;
private ObservableValue<Double> observable;
private StringProperty colorProperty = new SimpleStringProperty();
public ProgressCell() {
bar = new ProgressBar();
bar.setMaxWidth(Double.MAX_VALUE);
bar.setProgress(0f);
bar.styleProperty().bind(colorProperty);
}
public static <S> Callback<TableColumn<MyWrapper, Double>, TableCell<MyWrapper, Double>> forTableColumn() {
return param -> new ProgressCell();
}
@Override
protected void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
final TableColumn<MyWrapper, Double> column = getTableColumn();
observable = column == null ? null : column.getCellObservableValue(getIndex());
if (observable != null) {
bar.progressProperty().bind(observable);
} else if (item != null) {
bar.setProgress(item);
}
setGraphic(bar);
}
}
}

MyScene.fxml

<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.effect.Blend?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.StackPane?>
<AnchorPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="main.java.MyController">
<StackPane BorderPane.alignment="CENTER">
<children>
<TableView id="dataTable" fx:id="dataTable" prefHeight="193.0" prefWidth="678.0" snapToPixel="false">
<columns>
<TableColumn fx:id="nameColumn" editable="false" prefWidth="88.0" text="Name" />
<TableColumn fx:id="progressColumn" editable="false" prefWidth="75.0" text="Progress" />
</columns>
<effect>
<Blend />
</effect>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
</children>
</StackPane>
</AnchorPane>

我不知道如何在MyWrapper中添加progressProperty()方法的情况下使进度条正常工作。我期待进入progress的物业,比如name物业。有没有办法?你认为它会更好吗?

任何帮助表示赞赏。

不支持嵌套属性(正如您注意到的,我在一条神秘消失的评论中确认了...... - 在沿着树走的自定义 cellValueFactory 中提供属性是要走的路:只需对任务进度执行与对 bean 名称执行相同的操作。

一个工作代码片段:

// column setup
nameColumn.setCellValueFactory(cc -> cc.getValue().getMyBean().nameProperty());
progressColumn.setCellValueFactory(cc -> cc.getValue().getMyTask().progressProperty().asObject());
progressColumn.setCellFactory(ProgressBarTableCell.forTableColumn());
new Thread(w1.getMyTask()).start();

请注意DoublePropertyObjectProperty<Double>的转换(正如Slaw在评论中指出的那样,该评论也消失了;)

这种深入探讨是否是一个好主意取决于您的上下文:只要数据是只读的并且在其生命周期内不会更改,就可以了。否则,您需要采取预防措施来防止此类更改。无论如何,这都需要包装器中的附加逻辑,因此在该层中公开感兴趣的属性可能是更干净的方法。

抛出第一个错误是因为您的 MyObject 类没有 progressProperty 函数。

如果将此函数添加到包装类中,它将起作用。

public ReadOnlyDoubleProperty progressProperty() {
return task.progressProperty();
}  

.

progressCol.setCellValueFactory(new PropertyValueFactory<>("progress"));

最新更新