1 回答

TA貢獻2003條經驗 獲得超2個贊
JavaFX 有一個“事件線程”,負責處理按鈕點擊、更新標簽和任何其他與 GUI 相關的任務。當您調用時button.setOnAction(e -> doSomething());,當您按下按鈕時,doSomething()發生在 JavaFX 線程上。在此運行期間,不會發生其他 GUI 事件。這意味著您的界面將完全凍結,從而導致糟糕的用戶體驗。
此外,您不能在 JavaFX 線程以外的任何線程上執行 GUI 操作,否則您將獲得一個IllegalStateException. (試著打電話Executors.newSingleThreadExecutor().execute(() -> label.setText("hello"));看看這個在行動)
幸運的是,JavaFX 提供了解決這個問題的方法。
首先,也是最簡單的方法,是在新線程中調用長時間運行的方法(可能使用ExecutorService上面的 s),當您需要修改接口時,將這些調用包裝在對Platform.runLater(() -> updateInterface());. 這將發布updateInterface()到 GUI 線程,并允許它運行。
但是,這可能會很麻煩,因此首選方法是使用Service.
假設您的長時間運行的計算返回一個Double,您創建一個類 extending Service<Double>,覆蓋它的createTask()方法,并在那里執行計算,如下所示:
public class CalculationService extends Service<Double> {
? ? @Override
? ? protected Task<Double> createTask() {
? ? ? ? return new Task<Double>() {
? ? ? ? ? ? @Override
? ? ? ? ? ? protected Double call() throws Exception {
? ? ? ? ? ? ? ? return doCalculation();
? ? ? ? ? ? }
? ? ? ? };
? ? }
}
然后,在你的控制器中,聲明一個private final CalculationService service = new CalculationService();
在您的控制器initialize()方法中,您可以將此服務的輸出綁定到您想要的任何內容。例如:
calculationDisplayLabel.textProperty().bind(Bindings.createStringBinding(service.valueProperty()));
// continuously updates the label whenever the service calculates a new value
然后,每當您決定要重新開始計算時,service.restart()如果進程正在運行,您可以調用中斷進程,并從頭開始。
如果要在值更改時調用代碼,請向服務的值添加一個偵聽器。例如,如果您希望它在完成后立即重新計算,請調用:
service.valueProperty().addListener((obs, old, newValue) -> service.restart());
添加回答
舉報