Как связать вложенное свойство хода выполнения Задачи с TableView в JavaFX? - PullRequest
1 голос
/ 23 сентября 2019

Окружение: 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());
  ...
}

Это работало нормально.Но я хотел отделить Задачу от свойств bean-компонента, поэтому я решил создать своего рода оболочку, но больше не могу получить свойство progress.

EDIT

Пример кода:

MyApp

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();
    }
}

MyController

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();

}

MyWrapper

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();
    }

}

MyBean

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);
    }
}

MyTask

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;
    }

}

ProgressCell

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>

Я не знаюНе знаю, как заставить работать индикатор выполнения, не добавляя метод progressProperty() в MyWrapper.Я ожидал получить доступ к свойству progress, как свойство name.Есть ли способ?Как вы думаете, это будет лучше?

Любая помощь приветствуется.

Ответы [ 2 ]

0 голосов
/ 24 сентября 2019

Нет поддержки для вложенных свойств (как вы заметили, и я подтвердил в комментарии, что таинственно исчез ...) - предоставление свойства в настраиваемой ячейке cellValueFactory, которая спускается по дереву, является подходящим способом: просто сделайте то же самое дляход выполнения задачи, как вы делаете для имени компонента.

Фрагмент рабочего кода:

// 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();

Обратите внимание на преобразование DoubleProperty в ObjectProperty<Double> (как отметил Slawв комментарии, который также исчез;)

Является ли такое глубокое погружение хорошей идеей, зависит от вашего контекста: все в порядке, пока данные доступны только для чтения и не меняются в течение всего времени жизни.В противном случае вам нужно будет принять меры предосторожности, чтобы защититься от таких изменений.Что в любом случае потребует дополнительной логики в обертке, так что раскрытие интересующих нас свойств в этом слое, вероятно, будет более чистым подходом.

0 голосов
/ 23 сентября 2019

Первая ошибка выдается, потому что ваш класс MyObject не имеет функции progressProperty.

Если вы добавите эту функцию в свой класс-обертку, она будет работать.

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

.

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