【发布时间】:2012-11-14 15:30:31
【问题描述】:
类似的问题已经被问过好几次了。参见例如here 和 here。
但我真的很想了解为什么我的代码不起作用。正如在这个问题的其他版本中所回答的那样, 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 的看似多余的调用是为了安抚我提供的第二个链接上发布的建议,该链接指出: 在受影响最大的组件上调用 validate()。这可能是 Java 渲染周期中最混乱的部分。对 invalidate 的调用将组件及其所有祖先标记为需要布局。对 validate 的调用执行组件及其所有后代的布局。一个“向上”工作,另一个“向下”工作。您需要在树中将受您的更改影响的最高组件上调用 validate。 我认为这会导致它工作,但无济于事。
【问题讨论】:
-
虽然我没有相反的证据(除了
main方法),但与 UI 的所有交互都必须在 EDT 的上下文中执行。您可能正在这样做,但是您发表的一些声明让我感到担忧。如果您一开始是谨慎的,那么请随意忽略此声明...
标签: java swing event-dispatch-thread contentpane