背景信息
我想用Java版本1.6.0_26在Swing中构建一个工具/调色板窗口(也称为"浮动"窗口)。我认为JWindow是最好的选择,Swing文档也指出要使用JWindow(浮动窗口,有一个所有者框架,没有装饰,也没有窗口任务栏条目)。
我的浮动工具窗口的内容包括其他几个组件,如JButtons和JTextFields。
问题
当我点击浮动工具窗口时,所有者窗口(JFrame,我的"主应用程序窗口")偶尔会"闪烁"。"闪烁"看起来像是所有者窗口在几毫秒内失去了焦点,然后又恢复了焦点,导致窗口很快被禁用/启用(请注意,不会触发任何窗口事件,如焦点丢失或窗口被停用)。
我已经在Windows 7 64位和Windows XP下测试过了。
视频和示例代码
为了澄清这个问题(这有点难以解释),我拍了一段视频,在那里,当我反复点击浮动工具窗口时,你可以看到所有者窗口的"闪烁":
- http://goo.gl/wVEqJ(.MOV视频格式,我想你需要Quicktime来播放它)
我还整理了一个简单的示例代码来重现这个问题(这个代码在视频中使用):
import java.awt.*;
import javax.swing.*;
public class JWindowFlickerExample
{
public JWindowFlickerExample()
{
// I know swing code should be executed on the EDT,
// just wanted to keep it simple
// Create and show the "main application window"
JFrame frame = new JFrame( getClass().getSimpleName() );
frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
frame.setSize( 640, 480 );
frame.setLocationRelativeTo( null );
frame.setVisible( true );
// Create and show the "floating tool window"
MyFloatingToolWindow testWindow = new MyFloatingToolWindow( frame );
testWindow.setLocation( 400, 400 );
testWindow.setVisible( true );
}
public static void main( String[] args )
{
new JWindowFlickerExample();
}
@SuppressWarnings( "serial" )
private class MyFloatingToolWindow extends JWindow
{
public MyFloatingToolWindow( Window hostWindow )
{
super( hostWindow );
// setFocusableWindowState( false );
setSize( 300, 400 );
setLayout( null );
getContentPane().setBackground( Color.LIGHT_GRAY );
JTextField textField = new JTextField();
textField.setLocation( 50, 50 );
textField.setSize( 70, 30 );
add( textField );
}
}
}
迄今为止的进展
我还尝试将浮动工具窗口的"Window.setFocusableWindowState"设置为false。如果它是假的,就没有"闪烁",问题就消失了。该方法的JavaDoc指出:
将窗口的可聚焦状态设置为false是应用程序向AWT识别将用作浮动调色板或工具栏的窗口的标准机制,因此应该是不可聚焦的窗口。"
但是,我当然不能在浮动工具窗口中使用JTextField,因为我不能集中它(也许浮动工具窗口的文本字段不常见,但在我的情况下,这是必须的)。
我想"闪烁"效应在某种程度上与焦点管理有关。。。在那一瞬间,浮动工具窗口获得焦点,将其从所有者窗口移开,然后返回。但我不确定;附带说明:如果浮动工具窗口中的文本字段具有焦点,则所有者窗口将保持启用状态(这是正确的行为)。
我希望有一个简单的解决方案,这样我就可以把JWindow作为我的浮动工具窗口,把文本字段作为它的内容,因为除了所描述的"闪烁"问题之外,一切都很好。
我真的很感激任何帮助,非常感谢!
这个代码变体是否显示了相同的问题?(注意:在我开始改变它之前,我没有看到任何明显的闪烁。)
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
public class JWindowFlickerExample
{
public JWindowFlickerExample()
{
// I know swing code should be executed on the EDT,
// just wanted to keep it simple
// SOMETIMES 'KEEPING IT SIMPLE' CAN CAUSE THE PROBLEM!
SwingUtilities.invokeLater( new Runnable() {
public void run() {
// Create and show the "main application window"
JFrame frame = new JFrame( getClass().getSimpleName() );
frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
frame.pack();
frame.setSize( 640, 480 );
frame.setLocationRelativeTo( null );
frame.setVisible( true );
// Create and show the "floating tool window"
MyFloatingToolWindow testWindow = new MyFloatingToolWindow( frame );
testWindow.setLocation( 400, 400 );
testWindow.setVisible( true );
}
});
}
public static void main( String[] args )
{
new JWindowFlickerExample();
}
@SuppressWarnings( "serial" )
private class MyFloatingToolWindow extends JWindow
{
public MyFloatingToolWindow( Window hostWindow )
{
super( hostWindow );
JTextField textField = new JTextField(20);
JPanel p = new JPanel(new GridLayout());
p.setBackground( Color.GREEN );
p.setBorder(new EmptyBorder(40,40,40,40));
p.add(textField);
add( p );
pack();
}
}
}
我认为http://bugs.sun.com/view_bug.do?bug_id=4109702可能是相关的。
无论如何,这里有一个修复程序,似乎可以为我消除闪烁(Windows XP上的Java 1.6):
window = new JWindow(parentFrame);
window.setFocusableWindowState(false);
window.addComponentListener(new ComponentAdapter() {
@Override
public void componentShown(ComponentEvent e) {
window.setFocusableWindowState(true);
// Putting the focus on the content pane means that the first
// visible component isn't focused, but if the user tabs, they
// will get to it.
window.getContentPane().requestFocus();
}
@Override
public void componentHidden(ComponentEvent e) {
window.setFocusableWindowState(false);
}
});
诀窍似乎是让窗口在显示时不可聚焦。