javafx
Threading
Sök…
Uppdatering av användargränssnittet med Platform.runLater
Långdrivna operationer får inte köras på JavaFX-applikationstråden, eftersom detta förhindrar JavaFX från att uppdatera användargränssnittet, vilket resulterar i en frusen användargränssnitt.
Vidare måste varje ändring av en Node
som är en del av en "live" scengraf ske på JavaFX-applikationstråden. Platform.runLater
kan användas för att köra dessa uppdateringar i JavaFX-programtråden.
Följande exempel visar hur du uppdaterar en Text
Node
upprepade gånger från en annan tråd:
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);
}
}
Gruppera UI-uppdateringar
Följande kod gör att användargränssnittet svarar en kort stund efter knappklick, eftersom för många Platform.runLater
samtal används. (Försök bläddra i ListView
direkt efter att du har ListView
på knappen.)
@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();
}
För att förhindra detta istället för att använda ett stort antal uppdateringar använder följande kod en AnimationTimer
att köra uppdateringen endast en gång per ram:
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();
}
}
}
vilket tillåter gruppering av uppdateringarna med hjälp av klassen Updater
:
private final Updater updater = new Updater();
...
// Platform.runLater(() -> data.add(index));
updater.addTask(() -> data.add(index));
Hur man använder JavaFX Service
Istället för att köra intensiva uppgifter i JavaFX Thread
som bör göras till en Service
Så vad är egentligen en tjänst ?
En tjänst är en klass som skapar en ny Thread
varje gång du startar den och skickar en uppgift till den för att göra lite arbete. Tjänsten kan returnera eller inte ett värde.
Nedan är ett typiskt exempel på JavaFX Service som gör lite arbete och returnerar en
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;
}
};
}
}