Swing:更改隐藏帧上的标签,然后在 EDT 上以相反的顺序显示该帧
问题
我在 swing 中创建了一个对话框(JRE 6 update 10,Ubuntu linux)。当用户使用完该对话框后,该对话框将被隐藏。当用户单击另一个框架中的按钮时,框上的标签会根据该按钮更改,然后再次显示该框。
我遇到的问题是,该框在标签更改之前显示,即使以编程方式,我以相反的顺序进行调用。这会导致出现该框,然后出现标签更改,这在我们的慢速目标硬件上看起来“有问题”。看来 EDT 将帧 setVisible(true) 安排在标签 setText(....) 之前;它优先考虑此呼叫。有没有办法让 EDT 安排 setVisible(true) 在 setText(....) 之后执行?
请注意,该代码是通过单击已在 EDT 上执行的按钮调用的,因此无法使用 SwingUtilities.invokeAndWait。我已尝试使用 invokeLater 方法,但 EDT 仍然重新安排它。
重现
在 IDE 中以调试模式运行以下代码,并在显示和隐藏“对话框”框架后中断 showButton 的操作代码。标签的 setText(....) 更改不会立即对 GUI 产生影响,但框架的 setVisible(true) 会立即产生影响。然后单步执行 EDT,您将看到 setText 最终发生在 EDT 时间表的后面。
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.WindowConstants;
public class DemonstrateFramePaintEDTPriority {
static class MyFrame extends JFrame {
private JFrame frame;
private JLabel label;
int i = 0;
public MyFrame() {
// Some label strings
final String string[] = new String[] { "label text one",
"label 2222222", "3 3 3 3 3 3 3" };
// Create GUI components.
frame = new JFrame("Dialog");
label = new JLabel("no text set on this label yet");
frame.setSize(500, 200);
frame.setLayout(new FlowLayout());
frame.add(label);
// Add show and hide buttons.
JButton showButton = new JButton("show dialog");
showButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// Set the label text - THIS HAPPENS AFTER frame.setVisible
label.setText(string[i]);
// Select new label text for next time.
i++;
if (i >= string.length) {
i = 0;
}
// Show dialog - THIS HAPPENS BEFORE label.setText
frame.setVisible(true);
}
});
JButton hideButton = new JButton("hide dialog");
hideButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
label.setText("label removed");
frame.setVisible(false);
}
});
setSize(500, 200);
setLayout(new FlowLayout());
add(showButton);
add(hideButton);
}
}
public static void main(String[] args) {
JFrame frame = new MyFrame();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
}
The Problem
I create a dialog box in swing (JRE 6 update 10, Ubuntu linux). When the user has finished using the dialog box it is hidden. When the users click on a button in another frame, a label on the box is changed according to the button, and then the box is shown again.
The problem I'm having is that the box is shown before the label changes even though, programatically, I'm making the calls in the opposite order. This causes the box to appear followed by the label change which looks "glitchy" on our slow target HW. It appears that the EDT schedules the frame setVisible(true) ahead of the label setText(....); it gives priority to this call. Is there any way to get the EDT to schedule the setVisible(true) to execute after the setText(....)?
Note that the code is called from a button click which is already executing on the EDT so one can't use SwingUtilities.invokeAndWait. I've tried using the invokeLater method but the EDT still re-schedules it.
To reproduce
Run the following code in an IDE in debug mode and break in the showButton's action code after showing and hiding the "dialog" frame. The label's setText(....) change will not have an immediate effect on the GUI but the frame's setVisible(true) will. Then step through the EDT and you'll see that the setText eventually happens further down the EDT schedule.
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.WindowConstants;
public class DemonstrateFramePaintEDTPriority {
static class MyFrame extends JFrame {
private JFrame frame;
private JLabel label;
int i = 0;
public MyFrame() {
// Some label strings
final String string[] = new String[] { "label text one",
"label 2222222", "3 3 3 3 3 3 3" };
// Create GUI components.
frame = new JFrame("Dialog");
label = new JLabel("no text set on this label yet");
frame.setSize(500, 200);
frame.setLayout(new FlowLayout());
frame.add(label);
// Add show and hide buttons.
JButton showButton = new JButton("show dialog");
showButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// Set the label text - THIS HAPPENS AFTER frame.setVisible
label.setText(string[i]);
// Select new label text for next time.
i++;
if (i >= string.length) {
i = 0;
}
// Show dialog - THIS HAPPENS BEFORE label.setText
frame.setVisible(true);
}
});
JButton hideButton = new JButton("hide dialog");
hideButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
label.setText("label removed");
frame.setVisible(false);
}
});
setSize(500, 200);
setLayout(new FlowLayout());
add(showButton);
add(hideButton);
}
}
public static void main(String[] args) {
JFrame frame = new MyFrame();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我不认为这在 Linux 绘画中是一个问题。我可以在 Windows 7 64 位(JDK 1.6.0_18(早期访问))上重现您的问题。您非常接近答案 - 您了解 SwingUtilities.invokeLater,但您没有考虑在需要的地方使用它。
和我一起说吧:
如果不这样做,就会发生不好的事情。在这种情况下,您奇怪的重绘行为是由于未在 EDT 上创建 JFrame 和包含的组件而导致的。如果您将此行包含在 SwingUtilities.invokeLater 中,它将解决您的问题:
您是对的 - 您的 setText 发生在 EDT 上,但组件本身的初始化没有发生在 EDT 上,这就是根本原因。
当对给定代码是否发生在 EDT 上有疑问时,您可以使用 SwingUtilities.isEventDispatchThread() 来查明。
如果您计划进行大量 Swing 开发,我强烈建议您阅读 Filthy Rich Clients。我现在正在阅读它。
I don't think this is an issue in Linux painting. I can reproduce your issue on Windows 7 64-bit (JDK 1.6.0_18 (early access)). You're very close to the answer - you know about SwingUtilities.invokeLater, but you aren't thinking about using it where you need to.
Say it with me:
If you don't, bad things will happen. In this case, your weird repaint behavior is a result of not creating your JFrame and the contained components on the EDT. If you wrap this line in a SwingUtilities.invokeLater, it will fix your issue:
You are correct - your setText is happening on the EDT, but the initialization of the component itself didn't happen on the EDT, and that is the root cause.
When in doubt on whether given code happens on the EDT, you can use SwingUtilities.isEventDispatchThread() to find out.
I highly recommend reading Filthy Rich Clients if you plan on doing a lot of Swing development. I am in the process of reading it now.
问题不在于标签组件的文本没有改变。重画已经安排好了,但还没有发生。这和 Linux 都有打开窗口极其缓慢的趋势(窗口管理器或类似问题?)。 java.awt.EventQueue 按优先级调度,尽管我不记得细节了。
JComponent.paintImmediately
看起来是一个可能的方法。您可能想在 Swing/AWT 中找到有关动画的(好)文本。 (或者在没有窗口管理器的情况下运行。)The problem isn't that the label component's text hasn't change. It's that the repaint has been scheduled but hasn't happened yet. That and Linux has a tendency of being extremely slow to open windows (a problem with window manager or similar?). The
java.awt.EventQueue
schedules by priority, although I can't remember the details.JComponent.paintImmediately
looks like a likely method. You might want to find a (good) text on animation within Swing/AWT. (That or run without a window manager.)我刚刚在 OS X 上遇到了类似的问题。我的解决方案是:
这似乎强制 swing 在显示之前重新绘制内存中的帧。
I just encountered a similar problem on OS X. My solution was:
This appears to force swing to repaint the frame in memory before displaying it.