一般来说,调用setVisible 方法就足以使Swing 组件显示或隐藏。
为了确保它有效,我尝试了以下方法:
public class Visibility {
private void makeGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JLabel l = new JLabel("Hello");
final JButton b = new JButton("Hide Label");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
l.setVisible(false);
}
});
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(l, BorderLayout.CENTER);
f.getContentPane().add(b, BorderLayout.SOUTH);
f.setSize(200, 200);
f.setLocation(200, 200);
f.validate();
f.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Visibility().makeGUI();
}
});
}
}
上述程序可以通过点击JButton来影响可见性。
可能是线程问题吗?
我的下一个怀疑是,可能不在event dispatch thread (EDT) 上的Thread 可能不会立即影响显示,所以我在初始化JLabel 和JButton 后添加了以下内容。
Thread t = new Thread(new Runnable() {
public void run() {
while (true) {
b.setVisible(!b.isVisible());
try {
Thread.sleep(100);
} catch (InterruptedException e) { /* Handle exception /* }
}
}
});
t.start();
随着新的 Thread 运行,它每 100 毫秒更改一次 JLabel 的切换可见性,这也没有问题。
从event dispatch thread (EDT) 中调用 Swing 组件是一件坏事,因为 Swing 不是线程安全的。我有点惊讶它起作用了,它起作用的事实可能只是侥幸。
重绘JPanel?
如果 JLabel 的可见性仅在调整大小时受到影响,则可能意味着仅在重新绘制 JPanel 时才绘制 JLabel。
可以尝试的一件事是调用JPanel 的repaint 方法来查看JLabel 的可见性是否会改变。
但是这种方法似乎只是一种创可贴,如果主要原因是由于 EDT 试图更改 GUI 的线程而导致的。 (请注意,repaint 方法是线程安全的,因此可以由非 EDT 线程调用,但依赖 repaint 是一种变通方法而不是解决方案。)
尝试使用SwingUtilities.invokeLater
最后,我可能会尝试使用SwingUtilities.invokeLater 方法,如果它想影响 GUI,可以从与 EDT 分开运行的线程调用(并且只能调用)该方法。
所以,前面的Thread 例子应该写成:
Thread t = new Thread(new Runnable() {
public void run() {
while (true) {
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
b.setVisible(!b.isVisible());
}
});
} catch (Exception e1) { /* Handle exception */ }
try {
Thread.sleep(100);
} catch (InterruptedException e) { /* Handle exception */ }
}
}
});
t.start();
如果对 GUI 的更改确实发生在单独的线程上,那么我建议阅读 The Java Tutorials 中的 Lesson: Concurrency in Swing,以了解有关如何使用 Swing 编写表现良好的多线程代码的更多信息。