2013-05-23 16 views
5

Wiem, że zadanie ma metodę updateProgress, potrzebowałbym powiązać pasek postępu z zadaniem, jednak nie mogę tego zrobić, ponieważ nie mam paska postępu jako obiektu.JavaFX Aktualizacja paska postępu w widoku tabeli z zadania

Mój program ma TableView. Po wprowadzeniu adresu URL pobierania użytkownik kliknie przycisk Pobierz nowy wiersz utworzony w TableView. Wiersz zawiera kolumnę informacji i postępu. Następnie uruchamiam nowy wątek - zadanie. Tam, gdzie jest pobierane całe pobieranie i muszę jakoś zaktualizować pasek postępu w tym wierszu.

próbowałem wiążące SimpleDoubleProperty do zadania, ale nie aktualizuje pasek postępu ...

Odpowiedz

20

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.

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

    } 
} 

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.

+0

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

+0

Zaktualizowana odpowiedź, aby zapewnić dodatkowe informacje w oparciu o pytania w komentarzu Saras – jewelsea

+0

Dziękuję bardzo za poświęcony czas i tak głęboką odpowiedź. Bardzo mi to pomogło. – ksarunas

Powiązane problemy