ボールがウインドウ内を跳ねるプログラムを作っているのですが、ある所でフリーズしてしまいました。
class BallBound implements ActionListener {
BallBound() {
//ウインドウ作成・アクションリスナ登録・メニューバー作成&取り付けなど
}
//アニメーション
public void byoga() {
boolean xvect = true, yvect = true;
loop = true;//グローバル変数
while (loop) {
repaint();
try { Thread.sleep(10); }
catch (InterruptedException e1) { }
if (x <= 0 || x+vx >= frame.getWidth()) {
xvect = !xvect;
}//はみ出たら逆ベクトルへ
if (y <= 0 || y+vy >= frame.getHeight()) {
yvect = !yvect;
}
if (xvect) x += xch;
else x -= xch;
if (yvect) y += ych;
else y -= ych;
}
}
public void actionPerformed(ActionEvent e) {
if(e.getSource() == start) {
if(loop == false) {
byoga();
}
}
if(e.getSource() == stop) {
loop = false;
}
}
protected void paintComponent(Graphics g) {
g.fillOval(x, y, vx, vy);
}
}
という風にactionPerformed()から、JMenuItem startが押された場合にbyoga()を呼び出しているのですが、startのメニューを押した(選んだ)瞬間にフリーズしてしまってタスクマネージャから強制終了せざるを得ない状態になってしまいます。
しかしコンストラクタの末尾からbyoga()を呼び出してみたりmain()から呼び出したりした場合はフリーズせずに起動できます。
これは一体何故なんでしょうか・・・?
どなたか教えてほしいです・・・
No.2ベストアンサー
- 回答日時:
投稿されたプログラムでは、一つのメインスレッド上でビジネスロジックとGUI操作の両方をやろうとしているため、byoga()メソッドがリターンするまでrepaint()は実行されません。
そこで、ビジネスロジックを別スレッドにするとともに、本当はGUI操作をSwingUtilities.invokeLater()を経由する等によりGUIのスレッドに礼儀正しく登録委託する必要があります(下のサンプルプログラムは超単純で問題もないのでそれを省略)。ただし、そういう面倒なことをしたのは初期の話で、最近では、単純なアニメのようなものはjavax.swing.Timer, より複雑な(I/Oがからむような)ビジネスロジックはjavax.swing.SwingWorkerを使って実装するのが定石です。(参考URLはhttp://を省略。)
下のプログラムは、古典的に、明示的に別スレッドを作る解法です:
----------------------------------------------------
/* save and compile as BallBoundX.java */
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class BallBoundX extends JPanel {
int x, y, vx, vy, xch, ych;
boolean loop;
Thread t;
public BallBoundX(){
x = y= 0;
vx = vy = 20;
xch = ych = 5;
setBackground(Color.white);
setPreferredSize(new Dimension(500, 500));
}
public void start(){
if (t == null){
t = new Thread(new Runnable(){
public void run(){
byoga();
}
});
}
t.start();
}
public void stop(){
loop = false;
}
public void byoga() {
boolean xvect = true, yvect = true;
loop = true; //グローバル変数
while (loop) {
if (x < 0 || x + vx > getWidth()) {
xvect = !xvect;
} //はみ出たら逆ベクトルへ
if (y < 0 || y + vy > getHeight()) {
yvect = !yvect;
}
if (xvect){
x += xch;
}
else{
x -= xch;
}
if (yvect){
y += ych;
}
else{
y -= ych;
}
repaint();
try {
Thread.sleep(10);
}
catch (InterruptedException e1) {
e1.printStackTrace();
}
}
t = null;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.green);
g.fillOval(x, y, vx, vy);
}
public static void main(String[] args){
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container con = frame.getContentPane();
JMenuBar mb = new JMenuBar();
frame.setJMenuBar(mb);
JMenu menu = new JMenu("実行制御");
final JMenuItem start = new JMenuItem("start");
menu.add(start);
final JMenuItem stop = new JMenuItem("stop");
menu.add(stop);
mb.add(menu);
final BallBoundX bbx = new BallBoundX();
con.add(bbx, BorderLayout.CENTER);
frame.setBounds(100, 100, 500, 500);
frame.setVisible(true);
ActionListener ner = new ActionListener(){
public void actionPerformed(ActionEvent e){
if(e.getSource() == start) {
bbx.start();
}
else if(e.getSource() == stop) {
bbx.stop();
}
}
};
start.addActionListener(ner);
stop.addActionListener(ner);
}
}
-------------------------------------------------
なるほど。
まだJavaを初めて日が浅いのでスレッドなどには触れていませんでした。
このソースをコンパイルした時3つもクラスファイルが出来たことに驚いたぐらいの初心者です(笑
(ちなみにビジネスロジックという言葉も初めて聞きました。。。)
どうやらスレッドを使うと別個にクラスファイルが出来るのですね。
それにgetWidth(),getHeight()はJPanelのを使えば微妙にはみ出たりせず済むのですね。
本当はちょっと-50とかして微調整したりしてました。。。
ちょっとまだ完全に理解できていませんが、GUIとビジネスロジックは分けて書く、ということは覚えておこうと思います。
勉強になるソースをありがとうございました。
No.3
- 回答日時:
すいません。
急いで書いたプログラムのデバッグです:(1)t.start();はif節の中に入れないといけません。
(2)xvect,yvectはクラスグローバルとし、コンストラクタの中でtrueに初期化すべきです(そうしないと、stop -> 再startのとき、方向が前と違ってしまうことがある)。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- JavaScript プログラムがうまく動きませんレビューお願いします 1 2022/07/10 05:08
- JavaScript コードレビューをお願いします。 1 2022/07/16 05:38
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- Java java 引数 戻り値のあるメソッド 3 2023/02/12 06:23
- その他(プログラミング・Web制作) 十進BASICでの再帰についての質問です。 2 2022/11/18 09:17
- Visual Basic(VBA) [Excel VBA] このコードでは行の挿入や行の消去をすると13のエラーが出てしまう。 3 2022/12/09 00:29
- Excel(エクセル) エクセルVBAでオブジェクトが必要です 2 2022/09/10 16:37
- Visual Basic(VBA) 【VBA】写真の貼り付けコードがうまく機能しません。 5 2022/09/01 18:43
- Visual Basic(VBA) VBAのユーザーフォームのテキストボックスに入力制限をしたい 6 2022/11/15 08:28
- C言語・C++・C# C# DatagridviewにExcelシートを反映するとエラーが出る 2 2023/05/06 17:12
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
大量のデータを読み込んで表示...
-
長方形を描いて、それを移動さ...
-
未割り当てのローカル変数
-
C#から、C++で作成したdll内の...
-
LVM_SETITEMSTATEでListViewの...
-
C#でキーイベントが発生しない...
-
【java】座標の値をテキストフ...
-
ボタンのイベントで異なるウィ...
-
メンバ変数の隠蔽
-
c# スレッド間でのデータの共有
-
二分探索木の要素の数を数える...
-
C#で、あるクラスのメンバーす...
-
Java 他クラスの呼び出しが上手...
-
unityでのC++エラーの原因がわ...
-
C言語のポインターに関する警告
-
配列にnullを代入すると、null...
-
IF関数でEmpty値を設定する方法。
-
javaで質問です。 文字列2023/2...
-
System.err. printlnとSystem.o...
-
C#で動的配列Listの中身をListB...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
大量のデータを読み込んで表示...
-
ボタンの複数割り当てについて
-
C#で、あるクラスのメンバーす...
-
Junitテストでvoid戻り値メッソ...
-
C#でのWNetAddConnection3の使...
-
C# 矢印キーの取得
-
Thread.sleepのInterruptedExce...
-
unityでのC++エラーの原因がわ...
-
C#で別のFormへ複数の値を返そ...
-
C# visibleプロパティをfalseに...
-
エンターキーを押すとOKボタン...
-
Processingでマウスクリックで...
-
C#でキーイベントが発生しない...
-
[C#.net]スレッド化された別フ...
-
C# DataGridView列カスタマイズ
-
【C#】ソースコードをファイル...
-
LVM_SETITEMSTATEでListViewの...
-
GetDIBits関数の使い方について
-
未割り当てのローカル変数
-
C# MouseHoverを何度も呼ぶには
おすすめ情報