对话框关闭后焦点错误

时间:2015-08-19 11:40:56

标签: java dialog focus

我有一个应用程序,如果用户操作需要一定的时间,则会显示一个对话框,界面正在处理该操作。通常在操作完成后,对话框关闭,用户可以继续执行之前正在执行的操作。这似乎几乎总是有效,但是时不时地将焦点从聚焦的组件转移到焦点链中它后面2个位置的组件。

我无法创建一个小的示例程序来显示究竟发生了什么,但我已经能够将问题调试到我希望有人可以帮助我的地方。

似乎发生了这样的事情:

  1. sun.awt.TimedWindowEvent [WINDOW_GAINED_FOCUS,{main window} ...]事件由对话框处理并传递给DefaultKeyboardFocusManager(DKFM),后者将尝试将focusFocus恢复到主窗口。 / LI>
  2. DKFM告诉MostRecentFocusOwner专注于自己
  3. 该组件将自身注册为MostRecentFocusOwner,并尝试通过调用peer.requestFocus实际关注自身。
  4. *在WComponentPeer中,框架已聚焦但由于某种原因它返回false
  5. 组件看到它没有聚焦,所以它也返回false
  6. DKFM发现组件没有聚焦,所以它试图集中下一个组件
  7. 步骤3-5
  8. 另一个焦点事件触发,再次触发步骤1-7,但由于步骤3将新组件注册为MostRecentFocusOwner,因此循环遍历6中的组件,而下一个组件来自那里
  9. 另一个焦点事件触发从步骤1触发逻辑,但现在步骤4返回true。显示主窗口,现在聚焦了错误的组件。
  10. 对于4 *:在它工作的情况下似乎不同的东西和它不存在的情况是WComponentPeer在parentWindow的焦点请求之后检查,在正确的情况下,parentWindow.isFocused()是如果为true,则在失败的情况下,parentWindow.isFocused()将返回false。打开焦点记录时显示的日志记录是“rejectFocusRequestHelper [...]等待请求的异步处理”。
    这似乎表明WComponentPeer知道必须异步处理焦点请求的可能性,但DKFM却不知道。

    相关的堆栈跟踪如下:

    KeyboardFocusManager.setMostRecentFocusOwner(Window, Component) line: 1814  
    KeyboardFocusManager.setMostRecentFocusOwner(Component) line: 1801  
    TheComponent(Component).requestFocusHelper(boolean, boolean, CausedFocusEvent$Cause) line: 7618 (3)
    TheComponent(Component).requestFocusInWindow(CausedFocusEvent$Cause) line: 7533 
    DefaultKeyboardFocusManager.doRestoreFocus(Component, Component, boolean) line: 172 (6) 
    DefaultKeyboardFocusManager.restoreFocus(Window, Component, boolean) line: 151  (2)
    DefaultKeyboardFocusManager.restoreFocus(WindowEvent) line: 134 
    DefaultKeyboardFocusManager.dispatchEvent(AWTEvent) line: 302   
    ProgressDialog(Component).dispatchEventImpl(AWTEvent) line: 4731 (1)    
    

    所以我的问题是,我在这里查看Java错误吗?

    此外,文档提到您不应该假设更改焦点是同步完成的,但它不能解释何时它是同步的,何时不是因为触发焦点系统在Java中异步处理某些焦点请求而其他同步处理?

    编辑:它也可能是一个事件订购的东西。 在正确的情况下查看KeyboardFocusManager中的focusedWindow时,它是这样的:

    1. 主框架 - >空
    2. Null - >对话框
    3. 焦点变化(我希望对话)
    4. 对话框 - >空
    5. null - >主框架
    6. 焦点变化(到lastFocussedComponent
    7. 在失败的情况下,就像这样:

      1. 主框架 - >空
      2. 4x焦点变化(到主框架上的下一个可聚焦组件)
      3. null - >主框架
      4. 似乎在实际显示对话框之前将其删除。将主框架清除为活动窗口的事件在两种情况下都是WINDOW_FOCUS_LOST TimedWindowEvent,对话框为对面。

1 个答案:

答案 0 :(得分:0)

从我现在可以看出它是由Java bug引起的。因为我注意到对话框从未成为焦点,所以我能够在一个小型Java应用程序中重现它:

public class TestFocusStuff extends JFrame implements ActionListener
{
  private final MyDialog mDialog = new MyDialog();

  public static void main(String[] args)
  {
    TestFocusStuff tfs = new TestFocusStuff();
    tfs.setVisible(true);
  }

  public TestFocusStuff()
  {
    setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

    Container c = getContentPane();
    c.setLayout(new FlowLayout());
    for (int i = 0; i < 10; i++)
    {
      JTextField tf = new JTextField(10);
      c.add(tf);
      tf.addActionListener(this);
    }
    pack();
  }

  @Override
  public void actionPerformed(ActionEvent e)
  {
    mDialog.showQuickly();
  }

  private static class MyDialog extends JDialog
  {
    public void showQuickly()
    {
      setVisible(true);
      setVisible(false);
    }
  }
}

在任何文本字段中按Enter将弹出对话框并立即将其关闭。焦点会改变,就好像Tab被按两次一样。

如果我添加一个focuslistener并且在收到该事件后只关闭对话框,我希望这个应该修复。

编辑:我现在可以确认在关闭对话框之前等待WindowFocusEvent.windowGainedFocus确实有助于防止错误。