James D rozwiązać ten dział Oracle JavaFX w wątku: Table cell progress indicator. Właśnie skopiowałem to rozwiązanie do tej odpowiedzi.
Rozwiązanie tworzy wiele zadań i monitoruje ich postępy za pomocą zestawu pasków postępu w postaci TableView.
Oryginalny wątek zawiera również rozwiązanie, które używa ProgressIndicators w przypadku, gdy preferujesz te, do 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;
}
}
}
tekst wyjaśniający oparte na komentarz pytania
wystarczy tylko przeczytać tę sekcję, jeśli masz trudności ze zrozumieniem, jak powyższe prace kodu i chcesz zdobyć głębsze zrozumienie wartość komórki i połączenia własności.
Nie ma tu żadnego wiążącego (przynajmniej nie widzę).
Wiązanie (lub ChangeListener, co sprowadza się do tego samego) jest ukryty za realizację PropertyValueFactory i ProgressBarTableCell. Spójrzmy na odpowiednim kodem:
TableColumn<TestTask, Double> progressCol = new TableColumn("Progress");
progressCol.setCellValueFactory(
new PropertyValueFactory<TestTask, Double>("progress")
);
progressCol.setCellFactory(
ProgressBarTableCell.<TestTask> forTableColumn()
);
progressCol definiuje wziąć TestTask jako wiersza danych i wyodrębnić podwójna wartość z właściwości zadań testowych.
Fabryczna wartość komórki określa sposób wypełniania podwójnej wartości kolumny. Jest zdefiniowany na podstawie właściwości PropertyValueFactory, która przyjmuje parametr "postęp". Dzięki temu fabryka wartości właściwości będzie korzystać z konwencji nazewnictwa JavaFX i interfejsu Java Reflect API w celu wyszukiwania odpowiednich metod pobierania danych z instancji TestTask. W takim przypadku wywoła on metodę o nazwie progressProperty() w instancji TestTask w celu pobrania ReadOnlyDoubleProperty odzwierciedlającej postęp zadań.
Jak wskazuje dokumentacja, właściwość PropertyValueFactory jest krótką ręką dla bałaganu kodu poniżej, ale kluczowym faktem jest to, że zwraca wartość ObservableValue, której implementacja Table może użyć do ustawienia wartości komórki jako zmiany komórek.
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();
}
});
OK, więc teraz wartość komórki jest odzwierciedlana do podwójnej wartości postępu zadania, gdy zadanie robi postęp. Nadal jednak musimy graficznie przedstawić tę podwójną wartość. To właśnie robi ProgressBarTableCell. Jest to komórka tabeli, która zawiera pasek postępu.Metoda forTableColumn tworzy fabrykę, która generuje ProgressBarTableCells dla każdego niepustego wiersza w kolumnie i ustawia postęp paska postępu do wartości komórki, która została powiązana z właściwością postępu zadania przez właściwość PropertyValueFactory.
Mylące zrozumienie szczegółowej realizacji. . . pewnie. Ale te wysokopoziomowe fabryki i komórki pomocnicze zajmują się wieloma szczegółami powiązań niskiego poziomu, więc nie trzeba ich kodować w kółko iz punktu widzenia zwykłego wykorzystania API jest (miejmy nadzieję) proste i logiczny.
Również nie ma właściwości (np SimpleStringProperty itp) więc pytanie byłoby, co zrobić, jeśli muszę jak dwa więcej kolumn z SimpleStringProperty, jak dodać je do tego rodzaju Tableview?
Ponownie użyj właściwości PropertyValueFactory. Obraz Załóżmy, że masz właściwość ciąg o nazwie URL, a następnie można dodać kolumny tak:
TableColumn<TestTask, Double> urlCol = new TableColumn("URL");
urlCol.setCellValueFactory(
new PropertyValueFactory<TestTask, Double>("url")
);
pamiętać, że tylko potrzebna do ustawienia fabryczne wartość komórki, to dlatego, że fabryczny komórka w kolumnie zwróci komórka zawierająca etykietę, która bezpośrednio wyświetla wartość ciągu komórki.
teraz na powyższe działała poprawnie musimy metodę na TestTask który zapewnia link do tego zadania, na przykład:
final ReadOnlyStringWrapper url = new ReadOnlyStringWrapper();
public TestTask(String url) {
this.url.set(url);
}
public ReadOnlyStringProperty urlProperty() {
return url.getReadOnlyProperty()
}
Należy zauważyć, że konwencja nazewnictwa jest naprawdę ważne tutaj, to musi być urlProperty () nie może to być nic innego, a właściwość PropertyValueFactory nie znajdzie elementu dostępowego do właściwości.
Uwaga dla tych celów prosta wartość String z funkcją getUrl() działałaby równie dobrze jak właściwość, ponieważ właściwość PropertyValueFactory będzie działać z programem pobierającym, jak również z metodą właściwości. Jedyną zaletą korzystania z metody właściwości jest to, że pozwala ona aktualizować dane wartości tabeli w oparciu o zdarzenia zmiany właściwości, co nie jest możliwe w przypadku gettera prostego. Ale tutaj, ponieważ adres URL jest efektywny i nie zmienia się dla danego zadania, nie ma znaczenia, czy z tego zadania jest dostarczony getter lub metoda właściwości dla tego pliku.
Testowana ta aplikacja demonstracyjna - działa świetnie. Jednak dla mnie trudno jest zrozumieć ten kod. Nie ma tu żadnego wiążącego (przynajmniej nie widzę). Również nie ma żadnych właściwości (jak SimpleStringProperty itp.), Więc pytanie brzmiałoby, co jeśli potrzebuję jeszcze dwóch kolumn z SimpleStringProperty, jak mogę dodać je do tego rodzaju TableView? Z góry dziękuję. – ksarunas
Zaktualizowana odpowiedź, aby zapewnić dodatkowe informacje w oparciu o pytania w komentarzu Saras – jewelsea
Dziękuję bardzo za poświęcony czas i tak głęboką odpowiedź. Bardzo mi to pomogło. – ksarunas