JavaFX обновление progressbar в tableview от задачи
Я знаю, что задача имеет метод updateProgress, мне нужно было бы привязать progressbar к задаче, однако я не могу этого сделать, так как у меня нет progressbar в качестве объекта.
моя программа имеет TableView. Как только пользователь вводит url загрузки и нажимает загрузить новую строку, созданную в TableView. Строка имеет некоторую информацию и столбец progressbar. Затем я запускаю новую задачу thread. Где вся загрузка выполняется, и мне нужно как-то обновить индикатор выполнения в этой строке.
Я пробовал привязки SimpleDoubleProperty к задаче, но он не обновляет индикатор выполнения...
1 ответов
James D решил это в Oracle JavaFX forum thread:индикатор прогресса ячейки таблицы. Я только что скопировал это решение в этот ответ.
решение создает несколько задач и отслеживает их прогресс с помощью набора индикаторов прогресса в TableView.
исходный поток также включает в себя решение, которое использует ProgressIndicators в случае, если вы предпочитаете те ProgressBars.
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator ;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.ProgressBarTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class ProgressBarTableCellTest extends Application {
@Override
public void start(Stage primaryStage) {
TableView<TestTask> table = new TableView<TestTask>();
Random rng = new Random();
for (int i = 0; i < 20; i++) {
table.getItems().add(
new TestTask(rng.nextInt(3000) + 2000, rng.nextInt(30) + 20));
}
TableColumn<TestTask, String> statusCol = new TableColumn("Status");
statusCol.setCellValueFactory(new PropertyValueFactory<TestTask, String>(
"message"));
statusCol.setPrefWidth(75);
TableColumn<TestTask, Double> progressCol = new TableColumn("Progress");
progressCol.setCellValueFactory(new PropertyValueFactory<TestTask, Double>(
"progress"));
progressCol
.setCellFactory(ProgressBarTableCell.<TestTask> forTableColumn());
table.getColumns().addAll(statusCol, progressCol);
BorderPane root = new BorderPane();
root.setCenter(table);
primaryStage.setScene(new Scene(root));
primaryStage.show();
ExecutorService executor = Executors.newFixedThreadPool(table.getItems().size(), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}
});
for (TestTask task : table.getItems()) {
executor.execute(task);
}
}
public static void main(String[] args) {
launch(args);
}
static class TestTask extends Task<Void> {
private final int waitTime; // milliseconds
private final int pauseTime; // milliseconds
public static final int NUM_ITERATIONS = 100;
TestTask(int waitTime, int pauseTime) {
this.waitTime = waitTime;
this.pauseTime = pauseTime;
}
@Override
protected Void call() throws Exception {
this.updateProgress(ProgressIndicator.INDETERMINATE_PROGRESS, 1);
this.updateMessage("Waiting...");
Thread.sleep(waitTime);
this.updateMessage("Running...");
for (int i = 0; i < NUM_ITERATIONS; i++) {
updateProgress((1.0 * i) / NUM_ITERATIONS, 1);
Thread.sleep(pauseTime);
}
this.updateMessage("Done");
this.updateProgress(1, 1);
return null;
}
}
}
пояснительный текст, основанный на вопросах комментариев
вам нужно только прочитать этот раздел, если у вас возникли трудности с пониманием того, как работает приведенный выше код, и вы хотите получить более глубокое понимание значения ячейки и соединений свойств.
здесь нет никакой привязки (по крайней мере, я не вижу).
привязка (или ChangeListener, что составляет одно и то же) скрывается за реализацией PropertyValueFactory и ProgressBarTableCell. Давайте посмотрим на код:
TableColumn<TestTask, Double> progressCol = new TableColumn("Progress");
progressCol.setCellValueFactory(
new PropertyValueFactory<TestTask, Double>("progress")
);
progressCol.setCellFactory(
ProgressBarTableCell.<TestTask> forTableColumn()
);
progressCol определяется, чтобы взять TestTask в качестве строки данных и извлечь двойное значение из свойства test task.
фабрика значений ячеек определяет, как заполняется значение double для столбца. Он определяется на основе PropertyValueFactory, который принимает параметр "прогресс". Это говорит фабрике значений свойств использовать Соглашения об именах JavaFX и API отражения Java для поиска соответствующих методов для извлечения данных из экземпляра TestTask. В этом случае он вызовет метод с именем progressProperty() на экземпляре TestTask для получения ReadOnlyDoubleProperty, отражающего ход выполнения задач.
как говорится в его документации, PropertyValueFactory - это просто короткая рука для беспорядка код ниже, но ключевой факт заключается в том, что он возвращает ObservableValue, который реализация таблицы может использовать для установки значения ячейки при изменении ячейки.
TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name");
firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
// p.getValue() returns the Person instance for a particular TableView row
return p.getValue().firstNameProperty();
}
});
хорошо, теперь у нас есть значение ячейки, отраженное в двойном значении прогресса задачи всякий раз, когда задача делает какой-либо прогресс. Но нам все равно нужно графически представить это двойное значение. Это ProgressBarTableCell делает. Это ячейка таблицы, которая содержит индикатор выполнения. The forTableColumn метод создает фабрику, которая производит ProgressBarTableCells для каждой непустой строки в столбце и устанавливает прогресс индикатора выполнения, чтобы соответствовать значению ячейки, которая была связана со свойством прогресса задачи PropertyValueFactory.
путаница в понимании подробной реализации . . . конечно. Но эти высокопоставленные фабрики и клетки хелпера позаботятся о много низкоуровневые детали рычага для вас так, что вам не нужно их снова и снова и с простой точки зрения использования API это (надеюсь) проста и логична.
также нет свойств (например, SimpleStringProperty и т. д.) Итак, вопрос будет в том, что, если мне нужно еще два столбца с SimpleStringProperty, как добавить их в этот вид TableView?
используйте PropertyValueFactory еще раз. Давайте представим, что у вас есть строковое свойство URL, затем вы можете добавить столбцы вот так:
TableColumn<TestTask, Double> urlCol = new TableColumn("URL");
urlCol.setCellValueFactory(
new PropertyValueFactory<TestTask, Double>("url")
);
Примечание. нам нужно только установить фабрику значений ячеек, это потому, что фабрика ячеек по умолчанию для столбца вернет ячейку, содержащую метку, которая непосредственно отображает строковое значение ячейки.
теперь для правильной работы нам нужен метод TestTask, который предоставляет url-адрес для задачи, например:
final ReadOnlyStringWrapper url = new ReadOnlyStringWrapper();
public TestTask(String url) {
this.url.set(url);
}
public ReadOnlyStringProperty urlProperty() {
return url.getReadOnlyProperty()
}
обратите внимание, что соглашение об именах действительно важно здесь, это должен быть urlProperty() это не может быть ничего иначе или PropertyValueFactory не найдет метод доступа к значению свойства.
Примечание Для этих целей простое строковое значение с getUrl () работало бы так же хорошо, как свойство, поскольку PropertyValueFactory будет работать с геттером, а также методом свойства. Единственное преимущество использования метода property заключается в том, что он позволяет автоматически обновлять данные табличного значения на основе событий изменения свойств, что невозможно с прямым геттером. Но здесь, потому что url эффективно final и не изменяется для данной задачи, не имеет значения, предоставляется ли метод getter или property для этого файла из задачи.