JavaFX 2でアプリケーション作成(その8) - torutkのブログの続きです。
javafx.concurrent.Task(またはService)を使った別スレッド実行の結果を
GUIに反映させるには、Workerオブジェクト*1の状態変化をイベントとして取得します。
Workerオブジェクトの状態遷移
Workerオブジェクトは次の状態遷移を取ります。
● ↓ READY ---------------+ ↓ ↓ SCHEDULED --------→ CANCELED ↓ ______________↑ RUNNING -------+ ↓ ↓ SUCCEEDED FAILED
状態遷移が発生したタイミングで通知を受ける機能がServiceクラスに用意されています。
メソッド名 | 引数 | 通知が発生する遷移 |
---|---|---|
setOnCancelled | EventHandler |
CANCELED |
setOnFailed | ↓ | FAILED |
setOnReady | ↓ | READY |
setOnRunning | ↓ | RUNNING |
setOnScheduled | ↓ | SCHEDULED |
setOnSucceeded | ↓ | SUCCEEDED |
今回は、Semaphoreのacquireメソッド呼び出しが完了したタイミングを取得できればよいので、setOnSucceededで通知を受け取る設定をします。
追加コード
startメソッドに追記します。
acquireService = new AcquireService(semaphore); acquireService.setOnSucceeded(new EventHandler<WorkerStateEvent>() { @Override public void handle(WorkerStateEvent event) { updatePermites(); } });
これで一通りの動作を実装することができました。
ソースコード全体
ここまでのソースコードをまるごと次に示します。
SemaphoreView.java
import java.util.concurrent.Semaphore; import javafx.animation.FadeTransition; import javafx.application.Application; import javafx.concurrent.WorkerStateEvent; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ProgressBar; import javafx.scene.control.TextField; import javafx.scene.layout.BorderPane; import javafx.scene.layout.GridPane; import javafx.scene.text.Text; import javafx.stage.Stage; import javafx.util.Duration; public class SemaphoreView extends Application { private Semaphore semaphore = new Semaphore(1); private TextField permitsField; private Text resultText; private ProgressBar progressBar; private AcquireService acquireService; @Override public void start(Stage primaryStage) { primaryStage.setTitle("Semaphoreの動きを理解する"); BorderPane borderPane = new BorderPane(); Label topLabel = new Label("Semaphoreへの操作とその結果"); topLabel.setStyle("-fx-background-color: mistyrose"); BorderPane.setAlignment(topLabel, Pos.CENTER); BorderPane.setMargin(topLabel, new Insets(6)); borderPane.setTop(topLabel); GridPane semaphorePane = new GridPane(); BorderPane.setMargin(semaphorePane, new Insets(4, 12, 4, 12)); borderPane.setCenter(semaphorePane); semaphorePane.setHgap(10); semaphorePane.setVgap(16); Label permitsLabel = new Label("利用可能な許可数:"); semaphorePane.add(permitsLabel, 0, 0); permitsField = new TextField(); semaphorePane.setMargin(permitsField, new Insets(12, 6, 12, 6)); semaphorePane.add(permitsField, 1, 0); Button tryButton = new Button("Try acquire"); tryButton.setMaxWidth(Double.MAX_VALUE); tryButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { boolean acquired = semaphore.tryAcquire(); updateTryResult(acquired); updatePermites(); } }); semaphorePane.add(tryButton, 0, 1); resultText = new Text(); semaphorePane.setMargin(resultText, new Insets(12, 6, 12, 6)); semaphorePane.add(resultText, 1, 1); Button releaseButton = new Button("Release"); releaseButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { semaphore.release(); resultText.setText(""); updatePermites(); } }); releaseButton.setMaxWidth(Double.MAX_VALUE); semaphorePane.add(releaseButton, 0, 2); acquireService = new AcquireService(semaphore); acquireService.setOnSucceeded(new EventHandler<WorkerStateEvent>() { @Override public void handle(WorkerStateEvent event) { updatePermites(); } }); Button acquireButton = new Button("Acquire"); acquireButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { acquireService.restart(); } }); acquireButton.disableProperty().bind(acquireService.runningProperty()); acquireButton.setMaxWidth(Double.MAX_VALUE); semaphorePane.add(acquireButton, 0, 3); progressBar = new ProgressBar(0); progressBar.progressProperty().bind(acquireService.progressProperty()); progressBar.visibleProperty().bind(acquireService.runningProperty()); progressBar.setMaxWidth(Double.MAX_VALUE); semaphorePane.setMargin(progressBar, new Insets(12, 6, 12, 6)); semaphorePane.add(progressBar, 1, 3); borderPane.setCenter(semaphorePane); updatePermites(); primaryStage.setScene(new Scene(borderPane , 300 , 250)); primaryStage.show(); } private void updatePermites() { permitsField.setText(String.valueOf(semaphore.availablePermits())); } private void updateTryResult(boolean acquired) { resultText.setText(String.valueOf(acquired)); FadeTransition fader = new FadeTransition(Duration.millis(1000), resultText); fader.setFromValue(0); fader.setToValue(1); fader.play(); } public static void main(String[] args) { launch(args); } }
AcquireService.java
import java.util.concurrent.Semaphore; import javafx.concurrent.Task; import javafx.concurrent.Service; public class AcquireService extends Service { private Semaphore semaphore; public AcquireService(Semaphore semaphore) { this.semaphore = semaphore; } @Override protected Task createTask() { Task<Void> task = new Task<Void>() { @Override protected Void call() throws Exception { updateProgress(-1, -1); semaphore.acquire(); updateProgress(1, 1); return null; } }; return task; } }
次回は
CSSファイルによる見栄えの設定を行う予定です。
記事一覧
- JavaFX 2.0最初の一歩 - torutkのブログ
- JavaFX 2.0でアプリケーション作成(その1) - torutkのブログ
- JavaFX 2.0でアプリケーション作成(その2) - torutkのブログ
- JavaFX 2.0でアプリケーション作成(その3) - torutkのブログ
- JavaFX 2.0でアプリケーション作成(その4) - torutkのブログ
- JavaFX 2.0でアプリケーション作成(その5) - torutkのブログ
- JavaFX 2.0でアプリケーション作成(その6) - torutkのブログ
- JavaFX 2.0でアプリケーション作成(その7) - torutkのブログ
- JavaFX 2でアプリケーション作成(その8) - torutkのブログ
*1:TaskおよびServiceクラスはWorkerインタフェースを実装する