2013-05-23 9 views
5

Ich weiß Task hat eine Methode updateProgress, ich müsste Fortschrittsbalken an Task binden, aber ich kann das nicht tun, da ich keine Fortschrittsleiste als ein Objekt habe.JavaFX Update Fortschrittsbalken in der Tabellenansicht von Task

Mein Programm hat eine TableView. Sobald der Benutzer die Download-URL eingegeben hat, klickt er auf die in der TableView erstellte neue Zeile. Row hat einige Info und Fortschrittsbalken. Ich starte dann eine neue Thread-Aufgabe. Wo alle Downloads durchgeführt werden und ich den Fortschrittsbalken in dieser Zeile irgendwie aktualisieren muss.

versuchte ich SimpleDoubleProperty zu den Task-Bindung aber nicht Fortschrittsbalken aktualisieren ...

Antwort

20

James D löste dies in Oracle JavaFX Forum-Thread: Table cell progress indicator. Ich habe diese Lösung in diese Antwort kopiert.

Die Lösung erstellt mehrere Aufgaben und überwacht deren Fortschritt über eine Reihe von Fortschrittsbalken in einer TableView.

Der ursprüngliche Thread enthält auch eine Lösung, die ProgressIndicators verwendet, falls Sie diese zu ProgressBars bevorzugen.

progress

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

    } 
} 

Erläuterungstext Basierend auf Kommentar Fragen

Sie müssen diesen Abschnitt nur lesen, wenn Sie Schwierigkeiten haben, zu verstehen, wie die oben genannten Code funktioniert und wollen ein tieferes Verständnis gewinnen Zellwert- und Eigenschaftenverbindungen.

Es gibt hier keine Art von Bindung (zumindest sehe ich nicht).

Die Bindung (oder Change, was dasselbe ist) hinter der Umsetzung der PropertyValueFactory und die ProgressBarTableCell versteckt. Schauen sie sich den entsprechenden Code aussehen:

TableColumn<TestTask, Double> progressCol = new TableColumn("Progress"); 
progressCol.setCellValueFactory(
    new PropertyValueFactory<TestTask, Double>("progress") 
); 
progressCol.setCellFactory(
    ProgressBarTableCell.<TestTask> forTableColumn() 
); 

Die progressCol eine TestTask als Datenreihe nehmen definiert und extrahieren Eigenschaft einen doppelten Wert aus der Testaufgabe.

Die Zellenwertfactory definiert, wie der doppelte Wert für die Spalte aufgefüllt wird. Es wird basierend auf einer PropertyValueFactory definiert, die den Parameter "progress" verwendet. Dies weist die Eigenschaftswertfactory an, JavaFX-Namenskonventionen und die Java-Reflektions-API zu verwenden, um nach relevanten Methoden zum Abrufen der Daten von einer TestTask-Instanz zu suchen. In diesem Fall wird eine Methode namens progressProperty() für die TestTask-Instanz aufgerufen, um die ReadOnlyDoubleProperty abzurufen, die den Fortschritt der Aufgaben widerspiegelt. Wie es in seiner Dokumentation heißt, ist die PropertyValueFactory nur eine kurze Hand für die Unordnung des unten stehenden Codes, aber die entscheidende Tatsache ist, dass sie einen ObservableValue zurückgibt, den die Table-Implementierung verwenden kann, um den Wert der Zelle zu setzen Zellveränderungen.

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, so jetzt haben wir den Wert einer Zelle auf den doppelten Wert des Fortschritts der Aufgabe reflektiert wird, wenn die Aufgabe keine Fortschritte macht. Aber wir müssen diesen doppelten Wert irgendwie grafisch darstellen. Dies ist, was die ProgressBarTableCell tut. Es ist eine Tabellenzelle, die einen Fortschrittsbalken enthält.Die forTableColumn-Methode erstellt eine Factory, die die ProgressBarTableCells für jede nicht leere Zeile in der Spalte erstellt und den Fortschritt der Fortschrittsleiste so einstellt, dass sie dem Zellenwert entspricht, der mit PropValueFactory mit der Fortschrittseigenschaft der Aufgabe verknüpft wurde.

Verwirrend im Verständnis der detaillierten Implementierung. . . sicher. Aber diese High-Level-Hilfsfabriken und -zellen kümmern sich um eine Menge der Low-Level-Verknüpfungsdetails für Sie, so dass Sie sie nicht immer wieder neu codieren müssen und aus Sicht der einfachen API-Nutzung ist es (hoffentlich) einfach und logisch.

Auch ist es derzeit keine Immobilien (wie SimpleStringProperty etc.), so dass die Frage wäre, was ist, wenn ich wie zwei weitere Spalten mit SimpleStringProperty benötigen, wie füge ich sie diese Art von Tableview?

Verwenden Sie die PropertyValueFactory noch einmal. Lassen Sie uns Bild haben Sie eine String-Eigenschaft URL genannt wird, dann können Sie die Spalten wie folgt hinzu:

TableColumn<TestTask, Double> urlCol = new TableColumn("URL"); 
urlCol.setCellValueFactory(
    new PropertyValueFactory<TestTask, Double>("url") 
); 

Hinweis wir nur den Zellenwert Fabrik zu setzen benötigt, ist dies, weil die Standardzellfabrik für die Spalte wird wieder ein Zelle, die ein Label enthält, das direkt den Zeichenfolgenwert der Zelle anzeigt.

nun für die oben richtig arbeiten müssen wir eine Methode auf TestTask, die für die Aufgabe eine URL liefert, zum Beispiel:

final ReadOnlyStringWrapper url = new ReadOnlyStringWrapper(); 

public TestTask(String url) { 
    this.url.set(url); 
} 

public ReadOnlyStringProperty urlProperty() { 
    return url.getReadOnlyProperty() 
} 

Beachten Sie, dass die Namenskonvention hier wirklich wichtig ist, muss es urlProperty sein () Es kann nichts anderes sein oder die PropertyValueFactory findet den Eigenschaftswertaccessor nicht.

Hinweis für diese Zwecke hätte ein einfacher String-Wert mit einer getUrl() genauso gut funktioniert wie eine Eigenschaft, da PropertyValueFactory sowohl mit einer Getter- als auch einer Eigenschaftsmethode arbeitet. Der einzige Vorteil der Verwendung einer Eigenschaftsmethode besteht darin, dass die Tabellenwertdaten automatisch basierend auf Eigenschaftsänderungsereignissen aktualisiert werden können, was bei einem direkten Getter nicht möglich ist. Aber da die URL tatsächlich endgültig ist und sich für eine bestimmte Aufgabe nicht ändert, macht es keinen Unterschied, ob für diese Datei eine Getter- oder Eigenschaftsmethode von der Aufgabe bereitgestellt wird.

+0

Getestet diese Demo-App - es macht den Job einfach toll. Für mich ist es jedoch schwer, diesen Code zu verstehen. Es gibt hier keine Bindung (zumindest sehe ich nicht). Es gibt auch keine Eigenschaften (wie SimpleStringProperty usw.), also wäre die Frage, was ich tun möchte, wenn ich zwei weitere Spalten mit SimpleStringProperty benötige, wie füge ich sie zu dieser Art von TableView hinzu? Danke im Voraus. – ksarunas

+0

Aktualisierte Antwort, um zusätzliche Informationen basierend auf Fragen in Saras Kommentar – jewelsea

+0

Vielen Dank für Ihre Zeit und so eine tiefe Antwort. Es hat mir sehr geholfen. – ksarunas

Verwandte Themen