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

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

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

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

Javaアプレット

Javaアプレットは、Webページに組み込まれて実行されるJavaアプリケーションの形式です。Java SE 11で廃止となりました。

Q&A

解決済

1回答

2028閲覧

Javaのアプレットが途中で止まる

aiueoaiueoaiue

総合スコア94

Java

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

Javaアプレット

Javaアプレットは、Webページに組み込まれて実行されるJavaアプリケーションの形式です。Java SE 11で廃止となりました。

0グッド

1クリップ

投稿2018/01/13 16:18

編集2018/01/14 04:14

#問題
Javaのアプレットビューワーでタイマーを使った簡単な描画を行いたいのですが、途中までは動くのですが、急に止まってしまいます。自分なりに調べたところペイントメソッドが急に呼ばれなくなっているみたいです。この原因がわかる方、おられましたら回答お願い致します。
#ソース

Java

1import java.applet.*; 2import java.io.*; 3import javax.swing.*; 4import java.awt.Graphics; 5import java.awt.Dimension; 6import java.awt.*; 7import java.awt.event.*; 8 9 10/* 11<applet code="test.class" width="150" height="150"> 12</applet> 13*/ 14 15public class test extends Applet implements ActionListener{ 16 int X = 75, Y = 30, W = 30, H = 50, K = 10 ; 17 Timer timer, timer2; 18 int x; 19 Button btn = new Button("スタート"); 20 boolean btnClicked = false; 21 22 public void actionPerformed(ActionEvent e){ 23 if (e.getSource() == timer2&&this.btnClicked==true){ 24 x+= 2; 25 if(x == 8){ 26 x = 0; 27 } 28 } 29 if (e.getSource() == timer&&this.btnClicked==true){ 30 31 timer2.start(); 32 } 33 if (e.getSource()==this.btn){ 34 this.btnClicked = true; 35 this.timer.start(); 36 } 37 repaint() ; 38 } 39 @Override 40 public void init(){ 41 timer = new Timer(20, this); 42 timer2 = new Timer(2, this); 43 add(this.btn); 44 this.btn.addActionListener(this); 45 } 46 47 省略 48#追記 49timer2のインスタンスの生成をinitの中で行うことでアプレットは止まらなくなりました。

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

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

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

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

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

guest

回答1

0

ベストアンサー

###論理が不自然な点

timerが何度も発火するようになっていますので、発火の度にtimer2のインスタンスが繰り返し(無限に)生成され、そのたびにタイマーイベントの発火契機が増えていきます。

100や200程度のTimerインスタンスやタイマーイベントが登録された状態になったからといってjavax.swing.Timerのイベント通知機構が破綻するようなやわな作りにはなっていないと思いますが、それでも不必要にTimerインスタンスを生成してしまっているのは問題だと思います。

###アニメーションが止まった原因
それについては自分の環境(Windows10, 主記憶8GB, JDK 1.8.0_152)では再現しませんでした。
なお、IDE(IntelliJ IDEA)からのデバッグ実行によりAppletViewerの元で実行しています。

推測ですが、...

(1)timer2にTimer@1のインスタンスが設定されていてイベント発火を待機しているとする
(2)timerが発火してtimer2に新たなTimerインスタンスTimer@2が設定されてしまう
(3)古いインスタンスTimer@1のイベントが発火するがif (e.getSource() == timer2 ...)の判定で、timer2のインスタンスが変わってしまっているためにこの条件文が成立しない。ゆえにアニメーションの更新処理がされずにrepaintのみが実行される。

タイミングによって上記のような現象が起こっているのではないでしょうか?

###対処

(A) timerを何度も発火させる必要はない。=>ワンショットタイマーで充分。
この対策が本質的な対策だと思います。

しかし論理をわかりやすくするという意味で気になる点があるのでそちらも変更をお勧めします。

(B) 同一のイベントハンドラーで複数のイベントをまとめてハンドリングしようとすること

Event#getSource()によりイベント発火元に応じた処理を切り分けるという手法は「同じような処理を複数のコンポーネントに対して同一のハンドラーでまとめて処理する」というような場合なら便利です。例えばオセロのようなもので、駒をそれぞれ独立したコンポーネントにしておき、それら全てのクリックイベントを同一のハンドラーで処理するような場合です。イベントソースからどの駒であるかはわかるので、その違いを除き共通的なハンドリングを行いたくなることでしょう。

しかしながら本件ではボタンのクリック、timerの発火、timer2の発火のそれぞれはまったく違う処理ですので無理に一つのハンドラーで処理するのはかえって分かりにくくなると思います。たまたまだとは思いますがgetSource()を使ったために作りこまなくてよいバグを作ってしまったとも言えますし。

具体的にはアプレットのクラスからimplements ActionListenerを取り去り、ActionListenerのインスタンスをthisではなくて、それぞれのイベント設定ごとの個別のリスナー(ハンドラー)を無名クラスやラムダ式やメソッド参照を用いて設定することをお勧めします。

以下、ラムダ式の例を挙げてみます。 (ちなみにbtnClickedというフラグも必要ないように思えますので以下の例では省略してます。)

java

1// Javaではクラス名は必ず大文字で始めましょう。 2// 小文字で始めても動きはしますが、習慣に反しているため多くのプログラマーの目には 3// 大変奇妙に映ります。 4public class Test extends Applet { 5 // 定数ならstatic finalの方が良い 6 // (プログラムの今後の改造によっては定数ではなくなるのかも知れませんが) 7 static final int X = 75, Y = 30, W = 30, H = 50, K = 10 ; 8 int x; 9 10 @Override 11 public void init() { 12 Button btn = new Button("スタート"); 13 btn.addActionListener(ev -> onStartButtonClicked()); 14 add(btn); 15 } 16 17 // 何がおきたかはっきりわかるようなメソッド名にする 18 private void onStartButtonClicked() { 19 // timerはこのメソッド以外からさわる必要はないのでフィールドにしなくてよい 20 Timer timer = new Timer(20, event -> onAnimationStart()); 21 timer.setRepeats(false); // ワンショットタイマーにする 22 timer.start(); 23 } 24 25 // 現在のコードではtimer2をフィールドにする必要はないが 26 // 後でアニメーションを止めるような処理を追加することを想定すると 27 // 元のコードにあるとおり、フィールドにしておいた方がよいでしょう 28 Timer timer2; 29 30 private void onAnimationStart() { 31 timer2 = new Timer(2, event -> onUpdateAnimationFrame()); 32 timer2.start(); 33 } 34 35 private void onUpdateAnimationFrame() { 36 ...アニメーション用の状態変更処理... 37 repaint(); 38 } 39 40 ... 41}

投稿2018/01/14 02:15

編集2018/01/14 02:18
KSwordOfHaste

総合スコア18394

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

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

KSwordOfHaste

2018/01/14 02:25 編集

余談ですが、ご質問にあるコードはインデントに空白文字とタブ文字をまぜて使っているためご自分のエディター上ではそろっているように見えてもマークダウンによりタブ幅が変わるとインデントが乱れて見えてしまいます。こうした場で質問する際のコードは「閲覧者に正確にインデントを示し、コードを正確に把握しやすくする」ことに注意を払ってください。不正確なインデントは閲覧者へ無用な混乱を与えます。 ご質問にあるコードはそれほどの乱れはないですが、ひどい人になると「インデントがない方がまし」なようなハチャメチャなコードを貼り付けておられたりしますのでこうした点にも注意を払うとよいと思います。
aiueoaiueoaiue

2018/01/14 03:59

ありがとうございます。timer2のインスタンスをif文の中で生成していたのは最終的にタイマーのスピードを徐々に遅くしようと考えていたからです。そのためにはtimer2 = new Timer(2, this);の2秒遅延の部分を変数にして、いちいちインスタンスを生成していくしかないかなぁと考えていました。この方法が使えないとなると、どのようにすればタイマーのスピードを徐々に遅くするという事が出来るでしょうか? ちなみにtimer2のインスタンスの生成をinitの中で行うことでアプレットは止まらなくなり、思い通りの結果になりました。
KSwordOfHaste

2018/01/14 04:11

なるほど。そういう意図でしたか。 ところで、回答する際には質問文やその中のコードから読み取れる範囲でコメントするしかありません。おっしゃるとおりの意図があるなら質問文に明記して「なぜこういうコードを書きたかったのか」を示していただけると最初からそれを想定したコメントができます。 > どのようにすればタイマーのスピードを徐々に遅くするという事が出来るでしょうか? これは最初の質問にかかれていませんね?質問の内容を途中であれこれいじるのはよくありません。なぜかというと、質問内容が大きく変化すると編集後の質問内容と回答の内容が不一致になり、一般の閲覧者が「この質問に対してなんでこんな回答してるの?」と混乱するからです。元の質問に充分質問意図が含まれていなかった場合は質問意図を捕捉する意味で編集するのはよいことなのですが、内容が大きく変わってしまう場合は新たに質問を挙げて「知りたいことをより端的に尋ねる」方がよい場合もあります。このあたりの匙加減が難しいかもしれませんね。本件の場合、個人的には新たに質問を起こした方がよいと思います。タイトルを「Appletでアニメーションの時間間隔を徐々に変更したい」というズバリの内容にするといいのではないでしょうか?
aiueoaiueoaiue

2018/01/14 04:15

わかりました。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問