javafx
Gwintowanie
Szukaj…
Aktualizowanie interfejsu użytkownika za pomocą Platform.runLater
Operacje długo działające nie mogą być uruchamiane w wątku aplikacji JavaFX, ponieważ uniemożliwia to JavaFX aktualizację interfejsu użytkownika, co powoduje zawieszenie interfejsu.
Ponadto każda zmiana w Node
który jest częścią wykresu sceny „na żywo”, musi nastąpić w wątku aplikacji JavaFX. Platform.runLater
można wykorzystać do wykonania tych aktualizacji w wątku aplikacji JavaFX.
Poniższy przykład pokazuje, jak wielokrotnie aktualizować Node
Text
z innego wątku:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class CounterApp extends Application {
private int count = 0;
private final Text text = new Text(Integer.toString(count));
private void incrementCount() {
count++;
text.setText(Integer.toString(count));
}
@Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
root.getChildren().add(text);
Scene scene = new Scene(root, 200, 200);
// longrunning operation runs on different thread
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Runnable updater = new Runnable() {
@Override
public void run() {
incrementCount();
}
};
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
// UI update is run on the Application thread
Platform.runLater(updater);
}
}
});
// don't let thread prevent JVM shutdown
thread.setDaemon(true);
thread.start();
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Grupowanie aktualizacji interfejsu użytkownika
Poniższy kod powoduje, że interfejs użytkownika nie reaguje na chwilę po kliknięciu przycisku, ponieważ używanych jest zbyt wiele wywołań Platform.runLater
. (Spróbuj przewinąć ListView
natychmiast po kliknięciu przycisku).
@Override
public void start(Stage primaryStage) {
ObservableList<Integer> data = FXCollections.observableArrayList();
ListView<Integer> listView = new ListView<>(data);
Button btn = new Button("Say 'Hello World'");
btn.setOnAction((ActionEvent event) -> {
new Thread(() -> {
for (int i = 0; i < 100000; i++) {
final int index = i;
Platform.runLater(() -> data.add(index));
}
}).start();
});
Scene scene = new Scene(new VBox(listView, btn));
primaryStage.setScene(scene);
primaryStage.show();
}
Aby temu zapobiec, zamiast używać dużej liczby aktualizacji, poniższy kod używa AnimationTimer
do uruchomienia aktualizacji tylko raz na ramkę:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.animation.AnimationTimer;
public class Updater {
@FunctionalInterface
public static interface UpdateTask {
public void update() throws Exception;
}
private final List<UpdateTask> updates = new ArrayList<>();
private final AnimationTimer timer = new AnimationTimer() {
@Override
public void handle(long now) {
synchronized (updates) {
for (UpdateTask r : updates) {
try {
r.update();
} catch (Exception ex) {
Logger.getLogger(Updater.class.getName()).log(Level.SEVERE, null, ex);
}
}
updates.clear();
stop();
}
}
};
public void addTask(UpdateTask... tasks) {
synchronized (updates) {
updates.addAll(Arrays.asList(tasks));
timer.start();
}
}
}
który pozwala grupować aktualizacje za pomocą klasy Updater
:
private final Updater updater = new Updater();
...
// Platform.runLater(() -> data.add(index));
updater.addTask(() -> data.add(index));
Jak korzystać z usługi JavaFX
Zamiast uruchamiania intensywnych zadań w JavaFX Thread
które należy wykonać w Service
. Więc czym właściwie jest usługa ?
Usługa to klasa, która tworzy nowy Thread
każdym razem, gdy go uruchamiasz i przekazuje mu zadanie do wykonania pracy. Usługa może zwrócić wartość lub nie.
Poniżej znajduje się typowy przykład usługi JavaFX, która wykonuje pewne prace i zwraca
Map<String,String>(
):
public class WorkerService extends Service<Map<String, String>> {
/**
* Constructor
*/
public WorkerService () {
// if succeeded
setOnSucceeded(s -> {
//code if Service succeeds
});
// if failed
setOnFailed(fail -> {
//code it Service fails
});
//if cancelled
setOnCancelled(cancelled->{
//code if Service get's cancelled
});
}
/**
* This method starts the Service
*/
public void startTheService(){
if(!isRunning()){
//...
reset();
start();
}
}
@Override
protected Task<Map<String, String>> createTask() {
return new Task<Map<String, String>>() {
@Override
protected Void call() throws Exception {
//create a Map<String, String>
Map<String,String> map = new HashMap<>();
//create other variables here
try{
//some code here
//.....do your manipulation here
updateProgress(++currentProgress, totalProgress);
}
} catch (Exception ex) {
return null; //something bad happened so you have to do something instead of returning null
}
return map;
}
};
}
}