- ベストアンサー
JFrameクラスの挙動に関する質問と推察
- JFrameクラスを使用してWindowを表示するためのアプリを作成中ですが、repaintがうまく動作しません。
- Main文でrepaintを実行しても画面が更新されない原因として、JFrameが自動で作成したインスタンスとdrowインスタンスの間に関係がないことが考えられます。
- この問題を解決するためには、repaintを実行する際にJFrameのインスタンスと関連付ける必要があります。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
またまた#1です。 >これはswingが別のスレッドで動いているから、別の >スレッドからアクセスしてはいけないということですか? >それともMainからSwingが一連のスレッドなので、 >そもそも横からアクセスできないということですか? これの正解は、 >これはswingが別のスレッドで動いているから、別の >スレッドからアクセスしてはいけないということですか? こっちですね。 サンプルコードのMainFrameクラスのmainメソッド、およびDrowクラスのstartメソッドを見てみてください。 System.out.println("main = " + Thread.currentThread().getName()); System.out.println("GUI = " + Thread.currentThread().getName()); それぞれ、現在のスレッド名を表示するコードです。 実行してみるとわかると思いますが、結果は下記の通りです。 main = main GUI = AWT-EventQueue-0 Javaアプリケーションが実行されるとまず、mainと言う名前のスレッドが作られ、そのmainスレッドがmainメソッドを実行します。 (今回は、すぐにmainメソッドを抜けて終了してしまいます) しかし、JFrameなどのイベントで走っているスレッドは、これとは違います。 JFrameが表示されるときに、イベントディスパッチスレッドと呼ばれる専用のスレッドが作られ、GUIの描画とイベントの処理などは、このスレッドに任されます。 Swingは、複数のスレッドに同時実行されることを考慮されて作られていないシングルスレッド設計なので、このイベントディスパッチスレッド以外のスレッドが直接Swingコンポーネントにアクセスすると問題が発生する可能性があるのです。
その他の回答 (3)
- PecoPlus
- ベストアンサー率76% (144/188)
再び#1です。 メインとなるJFrameのクラスです。 アニメの機能をDrowにまとめたので、こちらは、すっきりわかりやすくすることができます。 また、DrowクラスはMainFrameクラスに依存していないので、ほかのクラスでも使い回すことができます。 (コンパイルするときは全角スペースを半角スペースに置換してからにしてください) public class MainFrame extends JFrame { private Drow drow; private JButton startButton; private JButton stopButton; public MainFrame() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); drow = new Drow(); getContentPane().add(drow, BorderLayout.CENTER); JPanel panel = new JPanel(); startButton = new JButton("Start"); startButton.addActionListener(new StartButtonClicked()); panel.add(startButton); stopButton = new JButton("Stop"); stopButton.addActionListener(new StopButtonClicked()); panel.add(stopButton); getContentPane().add(panel, BorderLayout.SOUTH); pack(); } private class StartButtonClicked implements ActionListener { public void actionPerformed(ActionEvent e) { drow.start(); } } private class StopButtonClicked implements ActionListener { public void actionPerformed(ActionEvent e) { drow.stop(); } } public static void main(String[] args) { System.out.println("main = " + Thread.currentThread().getName()); SwingUtilities.invokeLater(new Runnable() { public void run() { MainFrame frame = new MainFrame(); frame.setVisible(true); } }); } }
- PecoPlus
- ベストアンサー率76% (144/188)
こんばんは、#1です。 クラスの設計についてですが、クラスの親子関係が逆転しているように見えます。 まず、粒度の細かいクラスを作っていき、その親となるべきクラスが、そのインスタンスを保持するという形にしていくのが基本だと思います。 今回で言えば、丸を表示するクラスであるDrowは、部品となるべき言わば粒度の細かいクラスであり、できるだけ他のクラスに依存しないように、機能をまとめて実装します。 そして、親となるべきJFrameのクラスで、インスタンス化して、それを保持するという形がいいと思います。 実際どのようなものを作ろうとしておられるのかよくわからなかったので、丸が移動するサンプルを作ってみました。 まずは、Drowクラスです。 (コンパイルするときは全角スペースを半角スペースに置換してからにしてください) public class Drow extends JPanel { private int x; private int y; private Timer timer; public Drow() { timer = new Timer(33, new TimerAction()); x = 100; y = 150; setPreferredSize(new Dimension(600, 400)); } //アニメをスタートさせるメソッド public void start() { System.out.println("GUI = " + Thread.currentThread().getName()); x = 100; timer.start(); } //アニメをストップするメソッド public void stop() { timer.stop(); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.drawOval(x, y, 80, 80); } private class TimerAction implements ActionListener { public void actionPerformed(ActionEvent e) { if (x > 400) { timer.stop(); } else { x += 4; repaint(); } } } }
- PecoPlus
- ベストアンサー率76% (144/188)
こんばんは。 推察に関しては、その通りだと思います。 つまり、クラスの設計そのものが、ちょっと変ですね。 >また、このようなrepaintをMain文で実行したい >場合はどのようにしたらよいのでしょうか? mainメソッドで、repaintしようと言うことがそもそも間違っています。 mainメソッド内で、そのまま、repaint メソッドを呼ぶと言うことは、メインスレッドから swing コンポーネントにアクセスすると言うことで、シングルスレッド設計の swing に対してはしてはいけないことです。 円を動かすアニメーションか何かをしたいのだとしたら、javax.swing.Timer を GUI を構築するクラスに組み込んで使うべきだと思いますし、どうしても、メインメソッドからアクセスしたいのでしたら、SwingUtilities.invokeLater() を使うべきです。
補足
自分の理解力不足のため、いろいろと疑問点がでてきたのでさらに質問させていただきます。 <<つまり、クラスの設計そのものが、ちょっと変ですね。 これは自分のソースがへんってことですよね?どのようにするのが一般的なんですか? <<シングルスレッド設計の swing に対してはしてはいけないことです。 これはswingが別のスレッドで動いているから、別のスレッドからアクセスしてはいけないということですか?それともMainからSwingが一連のスレッドなので、そもそも横からアクセスできないということですか? <<javax.swing.Timer を GUI を構 これはこのAPIでrepaintを呼び出すということでいいんですよね? 質問攻めとなってしまいましたが、よろしければお返事をいただけるとうれしいです。
お礼
返事が遅くなってすいません。 わかりやすいサンプルコードを書いていただきありがとうございます。 JFrameなどのイベントで走っているスレッドはmainとは違うのですね。 おかげさまで解決することができました。長い間お付き合いいただきありがとうございました。