質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
JavaFX

JavaFXとは、Java仮想マシン上で動作するリッチインターネットアプリケーション (RIA) のGUIライブラリです。Swingとは異なり、FXMLと呼ばれる XMLとCSSを併用してデザインを記述します。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

2回答

2306閲覧

javafxを用いたカウントダウンについて

submaru

総合スコア18

JavaFX

JavaFXとは、Java仮想マシン上で動作するリッチインターネットアプリケーション (RIA) のGUIライブラリです。Swingとは異なり、FXMLと呼ばれる XMLとCSSを併用してデザインを記述します。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

1グッド

1クリップ

投稿2021/02/07 17:57

javafxの機能を用いて、ボタンを押したらカウントダウンが始まるというプログラムを作成しているのですが、ボタンを押すとlabelの文字が変化せずに秒数が経過してしまいます。どのようにプログラムを変更すればよいのでしょうか。

java

1import javafx.application.Application; 2import javafx.scene.control.Button; 3import javafx.scene.control.Label; 4import javafx.scene.layout.VBox; 5import javafx.scene.Scene; 6import javafx.stage.Stage; 7 8public class Test extends Application { 9 private Label label; 10 11 @Override 12 public void start(Stage stage) throws Exception { 13 this.label = new Label(); 14 label.setText("Count Down"); 15 Button b = new Button("start"); 16 17 VBox vbox = new VBox(); 18 vbox.getChildren().addAll(b, label); 19 20 b.setOnAction(event -> { 21 for (int i = 3; i > 0; i--) { 22 label.setText(String.valueOf(i)); 23 try { 24 Thread.sleep(1000); 25 } catch (Exception e) { 26 } 27 } 28 label.setText("start!"); 29 }); 30 31 stage.setScene(new Scene(vbox)); 32 stage.show(); 33 } 34}
TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/02/08 01:22 編集

うろ覚えだけど、JavaFXはmain関数と同じスレッドの画面アクセスと 同スレッド内での連続更新に制限(今回だとforループで1秒間隔更新処理)があって 別スレッドに分ける必要があったかと思います。 具体的には確か…別クラスとして画面を更新するコントローラクラスを置いて、さらにThreadクラスでタイマー管理をするのではなく、TimerTaskを使う感じで書いた覚えがあります。 これから仕事なんで夜にでもまた見に来ます。
guest

回答2

0

ベストアンサー

単純に考えると別スレッドでの実行です。
コントロール類には触れないので、Platform.runLater()等がいります。

Timelineでやるのもわかりやすいかもしれません。

注意)連打した時の挙動が違います。

Java

1import javafx.animation.KeyFrame; 2import javafx.animation.KeyValue; 3import javafx.animation.Timeline; 4import javafx.application.Application; 5import javafx.application.Platform; 6import javafx.scene.Scene; 7import javafx.scene.control.Button; 8import javafx.scene.control.Label; 9import javafx.scene.layout.VBox; 10import javafx.stage.Stage; 11import javafx.util.Duration; 12 13public class Test extends Application { 14 public static void main(String[] args) { launch(args); } 15 16 private Label label; 17 private Timeline timeline; 18 19 @Override 20 public void start(Stage stage) { 21 this.label = new Label(); 22 label.setText("Count Down"); 23 Button b = new Button("start"); 24 Button b2 = new Button("start"); 25 VBox vbox = new VBox(); 26 vbox.getChildren().addAll(b, b2, label); 27 28 29 b.setOnAction(event -> new Thread(() -> { 30 for (int i = 3; i > 0; i--) { 31 final int ii = i; 32 Platform.runLater(() -> label.setText(String.valueOf(ii))); 33 try { 34 Thread.sleep(1000); 35 } catch (Exception e) { } 36 } 37 Platform.runLater(() -> { 38 label.setText("start!"); 39 // なんかやる 40 }); 41 }).start()); 42 43 44 timeline = new Timeline( 45 new KeyFrame(Duration.seconds(0), new KeyValue(label.textProperty(), "3")), 46 new KeyFrame(Duration.seconds(1), new KeyValue(label.textProperty(), "2")), 47 new KeyFrame(Duration.seconds(2), new KeyValue(label.textProperty(), "1")), 48 new KeyFrame(Duration.seconds(3), new KeyValue(label.textProperty(), "start!"))); 49 timeline.setOnFinished(e -> { 50 // なんかやる 51 }); 52 b2.setOnAction(event -> timeline.play()); 53 54 stage.setScene(new Scene(vbox)); 55 stage.show(); 56 } 57}

投稿2021/02/08 14:56

TN8001

総合スコア9244

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

submaru

2021/02/11 16:34

timelineの方を利用して実装しました! ご回答ありがとうございました!
guest

0

イメージ説明

一応やりたいだろうことは出来ますね。
画面更新に対して色々面倒な制約がありますが、ここに書ききれるものでもないので参考リンクを張ります。

1. ポイント1. JavaFXメインスレッドで画面更新を行う

JavaFXにおけるスレッドの扱い - 軽Lab

メインスレッドを止めてしまうThread.sleep()により、メインスレッドが止まっている事、それから画面更新は処理を抜けてアイドル状態を作らないと起こらないらしい事。
つまりこの仕様のせいでラベルのテキストが更新されていなかったという事。
打開するには、空き時間にメインスレッドで遅延更新をするPlatform#runLater()を上手く使う事。

2. ポイント2. Threadの代わりにどうやってタイマー処理をするのか

ScheduledExecutorService 使い方メモ

まあ、色々あるんだけどScheduledExecutorService系のものを使うのが良いと思う。
実行タイミングなどが違ったりするので用途に応じて使い分ける感じですね。

3. ではどう直すか?

3.1. 問題点の整理

java

1b.setOnAction(event -> { 2 for (int i = 3; i > 0; i--) { 3 label.setText(String.valueOf(i)); 4 try { 5 Thread.sleep(1000); 6 } catch (Exception e) { 7 } 8 } 9 label.setText("start!"); 10});

label.setText(String.valueOf(i));
先述の通りメインスレッド処理中では更新できない。遅延更新させる必要がある。

Thread.sleep(1000);
こちらもメインスレッドを止めるだけなので使えない。ScheduledExecutorService等を使う必要がある。

for (int i = 3; i > 0; i--) { … }
前述2つの指摘により、そもそもforループは使えないので、発想を変える必要がある。
ScheduledExecutorServiceは実行する処理クラスにRunnableを使えるので、これをimplementsした実行クラスを作ればいい。(まあ、匿名インナークラスでもいいけど)

3.2. ScheduledExecutorServiceを使う場合

最初に断っておくが実現方法はこれ以外にもある。ここで書くつもりもないけど…。原稿料くれってなるし。
興味があれば他のScheduleServiceについても調べてみると良いと思う。

ScheduledExecutorService - JavaDoc11
Runnableインターフェースを使ったクラスを作って、それをどう実行するかというのがこのクラスの醍醐味。
一応、サボってこんな感じには書けるが、この場合は1秒間隔で延々とhelloと言い続けてしまう。
別途停止ボタンを置いて止める機構を作れればいいが今回の要件では使えない。
つまりカウントダウン終了時に自発的に止める工夫が必要となる。

java

1ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); 2service.scheduleWithFixedDelay(() -> { 3 System.out.println("hello!"); 4}, 1, 1, TimeUnit.SECONDS);

まあ、ぱっと浮かんだのはScheduledExecutorServiceのタスクを止めるには、ScheduledExecutorService#shutdown()を呼ぶ必要がある為、
サービスインスタンスを炊く制するタスクに持たせて、カウントダウン終了条件を満たしたらshutdown()を呼ぶようにすれば良いだろう。

そうすると呼び出し側はこんなイメージになって。

java

1ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); 2service.scheduleWithFixedDelay(new MyTask(), 1, 1, TimeUnit.SECONDS);

呼び出される側(ラベル更新を行う実行犯)はこんな感じになる。

java

1class MyTask implements Runnable { 2 // クラスメンバその1 3 // クラスメンバその2 4 // クラスメンバその3 5 6 /** 7 * ラベルを一定間隔でカウントダウン表示する為のタスク 8 * @param service スケジュールサービス 9 * @param count カウント開始値 10 * @param label 更新するラベル 11 */ 12 public MyTask(ScheduledExecutorService service, int count, Label label) { 13 // クラスメンバの初期化処理を書く 14 } 15 16 @Override 17 public void run() { 18 // ラベルを更新する処理 19 // カウントダウンする処理 20 // 終了条件判定でshutdownさせるかどうかの処理 21 } 22}

以上です。

ちなみに、コードを書くためのヒントは出しますが
私の実装コードを現時点で出すつもりはありません。
悪しからず宜しくお願いします。

投稿2021/02/08 12:50

編集2021/02/08 12:54
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問