これと似たような質問が何度かありました。たとえば、こことここを参照してください。
それでも、私のコードが機能しない理由を本当に理解したいと思います。この質問の他のバージョンで回答されているように、おそらく CardLayout で十分ですが、私の場合、それが理想的かどうかはわかりません。いずれにせよ、私が興味を持っているのは、なぜこれが機能しないのかを概念的に理解することです。
コンテンツ ペインが主要なイベントをリッスンする JFrame があります。コンテンツ ペインでキーが押されると、コンテンツ ペインは、新しいコンテンツ ペインで自身を更新するよう JFrame に指示します。問題の簡単な例を次に示します。
このコードは完全にコンパイル可能です。コピペしてそのまま実行できます。
これが私のJFrameです:
import javax.swing.*;
import java.awt.*;
import java.util.Random;
public class SimpleSim extends JFrame{
private static SimpleSim instance = null;
public static SimpleSim getInstance(){
if(instance == null){
instance = new SimpleSim();
}
return instance;
}
private SimpleSim(){}
public void initialize(){
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setExtendedState(Frame.MAXIMIZED_BOTH);
this.pack();
this.setVisible(true);
update();
}
public void update(){
System.out.println("SIMPLE_SIM UPDATE THREAD: " + Thread.currentThread().getName());
Random rand = new Random();
float r = rand.nextFloat();
float g = rand.nextFloat();
float b = rand.nextFloat();
SimplePanel simplePanel = new SimplePanel(new Color(r, g, b));
JPanel contentPane = (JPanel) this.getContentPane();
contentPane.removeAll();
contentPane.add(simplePanel);
contentPane.revalidate();
contentPane.repaint();
validate();
repaint();
}
}
コンテンツ ペインとして機能する JPanel は次のとおりです。
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class SimplePanel extends JPanel implements KeyListener {
public SimplePanel(Color c){
setFocusable(true);
setLayout(null);
setBackground(c);
setVisible(true);
this.addKeyListener(this);
}
public void keyTyped(KeyEvent keyEvent) {
if(keyEvent.getKeyChar() == 'a'){
System.out.println("a");
System.out.println("SIMPLE_PANEL KEY PRESS THREAD: " + Thread.currentThread().getName());
SimpleSim.getInstance().update();
}
}
public void keyPressed(KeyEvent keyEvent) {
}
public void keyReleased(KeyEvent keyEvent) {
}
}
奇妙なことに、最初に を押したときは機能しますが、その後は機能a
しません。私の推測では、ここでスレッドの問題が発生しています。update
が最初に呼び出されたときに、メイン スレッドで呼び出されていることがわかります。次に EDT で呼び出されたとき。invokeLater() を使用して update() を呼び出してみましたが、それも機能しませんでした。別の設計パターンを使用した回避策を見つけましたが、なぜこれが機能しないのかを知りたいです。
また、実行する単純なクラス:
public class Run {
public static void main(String[] args){
SimpleSim.getInstance().initialize();
}
}
注: JFrame を検証して再描画するための一見冗長な呼び出しは、私が提供した 2 番目のリンクに投稿されたアドバイスをなだめるために行われました 。これはおそらく、Java のレンダリング サイクルの最も複雑な部分です。invalidate の呼び出しは、コンポーネントとそのすべての祖先をレイアウトが必要であるとマークします。validate の呼び出しは、コンポーネントとそのすべての子孫のレイアウトを実行します。1 つは「上」で動作し、もう 1 つは「下」で動作します。変更の影響を受けるツリーの最上位のコンポーネントで validate を呼び出す必要があります。これでうまくいくと思ったのですが、うまくいきませんでした。