将JWindow用于浮动工具窗口会导致所有者窗口在单击浮动窗口时“闪烁”

时间:2012-02-11 16:11:44

标签: java swing focus flicker jwindow

背景资料

我想在Swing中使用 Java Version 1.6.0_26 构建一个工具/调色板窗口(也称为“浮动”窗口)。我认为JWindow是最好的选择,并且Swing文档指出使用JWindow用于此类目的(浮动窗口,其具有所有者框架,没有装饰且没有窗口任务栏条目)。

我的浮动工具窗口的内容包含其他几个组件,如JButtons和JTextFields。

问题

当我点击浮动工具窗口时,所有者窗口(JFrame,我的“主应用程序窗口”)偶尔会“闪烁”。 “闪烁”看起来像所有者窗口正在失去焦点几毫秒而不是获得焦点,导致窗口非常快速禁用/启用(请注意,没有窗口事件被触发,如焦点丢失或窗口-deactivated)。

我在Windows 7 64位和Windows XP下测试了这个。

视频和示例代码

为了澄清问题(这有点难以解释),我拍摄了一段视频,当我点击浮动工具窗口时,你可以看到所有者窗口的“闪烁”:

我还汇总了一个简单的示例代码来重现问题(此代码在视频中使用):

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作为我的浮动工具窗口并使用文本字段作为内容 - 因为除了所描述的“闪烁”问题之外,一切都很有效。

非常感谢任何帮助,非常感谢!

2 个答案:

答案 0 :(得分:2)

此代码变体是否显示相同的问题? (注意:在我开始更改之前,我没有看到任何明显的闪烁。)

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();
        }
    }
}

答案 1 :(得分:0)

我认为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);
    }
});

诀窍似乎是让的窗口在显示时可以聚焦。