对话框关闭后字段焦点错误
Wrong field focussed after dialog closes
我有一个应用程序,如果用户操作需要一定的时间,则会显示一个对话框,表明界面正在处理该操作。通常当操作完成后,对话框关闭,用户可以继续做他之前正在做的事情。这似乎几乎总是有效,但有时焦点会从焦点组件转移到焦点链中位于其后 2 位的组件。
我无法创建一个小的示例程序来展示到底发生了什么,但我已经能够将问题调试到我希望有人能帮助我的地步。
似乎发生的事情是这样的:
- A sun.awt.TimedWindowEvent[WINDOW_GAINED_FOCUS, {main window}...] 事件由对话框处理并传递给 DefaultKeyboardFocusManager (DKFM),它将尝试恢复焦点到主要 window.
- DKFM 告诉 MostRecentFocusOwner 自己聚焦
- 该组件将自己注册为 MostRecentFocusOwner 并尝试通过调用 peer.requestFocus.
来实际聚焦自己
- *在 WComponentPeer 中,框架被聚焦但由于某种原因它 returns false
- 该组件发现它没有获得焦点,所以它 return 也是错误的
- DKFM 发现该组件未获得焦点,因此它尝试关注下一个组件
- 步骤 3-5
- 另一个焦点事件触发,再次触发第 1-7 步,但因为第 3 步将新组件注册为 MostRecentFocusOwner,所以循环遍历 6 的组件和下一个组件
- 另一个焦点事件触发了第 1 步中的逻辑,但现在第 4 步 return 为真。显示了主要 window,现在聚焦了错误的组件。
对于 4*:在它工作和不工作的情况下似乎有所不同的是 WComponentPeer 在对 parentWindow 的焦点请求之后进行检查,在正确的情况下 parentWindow.isFocused() 为真,在失败的情况下 parentWindow.isFocused() 将 return 为假。当我打开焦点日志记录时显示的日志记录是 "rejectFocusRequestHelper [...] Waiting for asynchronous processing of the request"。
这似乎表明 WComponentPeer 知道必须异步处理焦点请求的可能性,但 DKFM 不知道。
相关stacktrace如下:
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 时,它是这样的:
- 主框架 -> 空
- 空 -> 对话框
- 焦点改变(我希望对话)
- 对话框 -> 空
- null -> 主框架
- 焦点变化(到 lastFocussedComponent
在失败的情况下是这样的:
- 主框架 -> 空
- 4x 焦点变化(到主框架上的下一个可聚焦组件)
- null -> 主框架
似乎对话框在实际显示之前被删除了。将主框架清除为活动 window 的事件在这两种情况下都是 WINDOW_FOCUS_LOST TimedWindowEvent,对话框与之相反。
据我所知,这是由 Java 错误引起的。因为我注意到对话框从未成为焦点,所以我能够在一个小的 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 键两次一样改变。
我希望如果我添加一个焦点侦听器并仅在收到该事件后关闭对话框,这个问题应该得到解决。
编辑:我现在可以确认在关闭对话框之前等待 WindowFocusEvent.windowGainedFocus 确实有助于防止错误。
我有一个应用程序,如果用户操作需要一定的时间,则会显示一个对话框,表明界面正在处理该操作。通常当操作完成后,对话框关闭,用户可以继续做他之前正在做的事情。这似乎几乎总是有效,但有时焦点会从焦点组件转移到焦点链中位于其后 2 位的组件。
我无法创建一个小的示例程序来展示到底发生了什么,但我已经能够将问题调试到我希望有人能帮助我的地步。
似乎发生的事情是这样的:
- A sun.awt.TimedWindowEvent[WINDOW_GAINED_FOCUS, {main window}...] 事件由对话框处理并传递给 DefaultKeyboardFocusManager (DKFM),它将尝试恢复焦点到主要 window.
- DKFM 告诉 MostRecentFocusOwner 自己聚焦
- 该组件将自己注册为 MostRecentFocusOwner 并尝试通过调用 peer.requestFocus.
- *在 WComponentPeer 中,框架被聚焦但由于某种原因它 returns false
- 该组件发现它没有获得焦点,所以它 return 也是错误的
- DKFM 发现该组件未获得焦点,因此它尝试关注下一个组件
- 步骤 3-5
- 另一个焦点事件触发,再次触发第 1-7 步,但因为第 3 步将新组件注册为 MostRecentFocusOwner,所以循环遍历 6 的组件和下一个组件
- 另一个焦点事件触发了第 1 步中的逻辑,但现在第 4 步 return 为真。显示了主要 window,现在聚焦了错误的组件。
对于 4*:在它工作和不工作的情况下似乎有所不同的是 WComponentPeer 在对 parentWindow 的焦点请求之后进行检查,在正确的情况下 parentWindow.isFocused() 为真,在失败的情况下 parentWindow.isFocused() 将 return 为假。当我打开焦点日志记录时显示的日志记录是 "rejectFocusRequestHelper [...] Waiting for asynchronous processing of the request"。
这似乎表明 WComponentPeer 知道必须异步处理焦点请求的可能性,但 DKFM 不知道。
相关stacktrace如下:
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 时,它是这样的:
- 主框架 -> 空
- 空 -> 对话框
- 焦点改变(我希望对话)
- 对话框 -> 空
- null -> 主框架
- 焦点变化(到 lastFocussedComponent
在失败的情况下是这样的:
- 主框架 -> 空
- 4x 焦点变化(到主框架上的下一个可聚焦组件)
- null -> 主框架
似乎对话框在实际显示之前被删除了。将主框架清除为活动 window 的事件在这两种情况下都是 WINDOW_FOCUS_LOST TimedWindowEvent,对话框与之相反。
据我所知,这是由 Java 错误引起的。因为我注意到对话框从未成为焦点,所以我能够在一个小的 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 键两次一样改变。
我希望如果我添加一个焦点侦听器并仅在收到该事件后关闭对话框,这个问题应该得到解决。
编辑:我现在可以确认在关闭对话框之前等待 WindowFocusEvent.windowGainedFocus 确实有助于防止错误。