但我真的很想理解为什么我的代码不起作用。正如在这个问题的其他版本中已经回答的那样,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 。 我认为这会导致它起作用,但无济于事。
答案 0 :(得分:4)
我对您的代码进行了一些修改,抱歉,但它使测试SOooo变得更加容易......
我可以看到的导入更改是在update方法中。基本上我只是在框架上调用revalidate
重新验证状态
将组件层次结构重新验证到最近的验证根目录。
此方法首先使组件层次结构无效 此组件最近的验证根。之后, 组件层次结构从最近的验证开始验证 根
这是一种可以帮助应用程序开发人员的便捷方法 避免手动查找验证根。基本上,它是等价的 首先在此组件上调用invalidate()方法,然后 在最近的验证根上调用 validate()方法。
我认为最后一部分是你自己的代码中缺少的部分。
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.setSize(400, 400);
this.setVisible(true);
setLayout(new BorderLayout());
update();
}
public void update() {
System.out.println("NEXT: " + 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();
getContentPane().removeAll();
add(simplePanel);
revalidate();
}
public class SimplePanel extends JPanel {
public SimplePanel(Color c) {
setFocusable(true);
setLayout(null);
setBackground(c);
setVisible(true);
requestFocusInWindow();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), "A");
am.put("A", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("a");
System.out.println("KEY: " + Thread.currentThread().getName());
SimpleSim.getInstance().update();
}
});
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
SimpleSim.getInstance().initialize();
}
});
}
}
此外,我建议您利用key bindings API而不是KeyListener
。它将解决一些焦点问题;)
<强>更新强>
经过一段时间测试各种排列,我们得出结论,主要问题与焦点问题有关。
虽然SimplePanel
是可聚焦的,但没有任何东西可以让它集中注意力,这意味着关键监听器无法被触发。
添加simplePanel.requestFocusInWindow
它添加到框架之后似乎允许密钥监听器保持活动状态。
根据我自己的测试,在调用revalidate
时,面板没有更新。