我正在使用自定义JComboBox作为JTable中的单元格编辑器。 当用户使用键盘控件到达单元格时,它会尝试打开弹出窗口。 这会导致以下错误:
java.awt.IllegalComponentStateException: component must be showing on the screen to determine its location
at java.awt.Component.getLocationOnScreen_NoTreeLock(Component.java:1964)
at java.awt.Component.getLocationOnScreen(Component.java:1938)
at javax.swing.JPopupMenu.show(JPopupMenu.java:887)
at javax.swing.plaf.basic.BasicComboPopup.show(BasicComboPopup.java:191)
at javax.swing.plaf.basic.BasicComboBoxUI.setPopupVisible(BasicComboBoxUI.java:859)
at javax.swing.JComboBox.setPopupVisible(JComboBox.java:796)
我看过一些文章,指出这是一个已知问题,解决方案是设置:
comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
然而,这无济于事。 这到底是怎么回事?
我读过的所有关于这个问题的帖子和文章都对问题的本质非常模糊。
有没有人深入了解为什么会出现这个问题的性质? 我的组合框非常自定义,因此有助于了解问题的基础,以便我可以修复代码。
这是在组合框上的焦点获得事件触发的,该事件被捕获并调用 setPopupVisible(true(;
public void focusGained(java.awt.event.FocusEvent e)
{
//if focus is gained then make sure we show the popup if it is suppose to be visible
setPopupVisible(true);
//and highlight the selected text if any
comboTextEditor.setCaretPosition(comboTextEditor.getText().length());
comboTextEditor.moveCaretPosition(0);
}
顺便说一下,我在 Java 1.7_40 中得到的结果与 Java 1.6_45 相同
全栈跟踪:
Exception in thread "AWT-EventQueue-1" java.awt.IllegalComponentStateException: component must be showing on the screen to determine its location
at java.awt.Component.getLocationOnScreen_NoTreeLock(Component.java:1964)
at java.awt.Component.getLocationOnScreen(Component.java:1938)
at javax.swing.JPopupMenu.show(JPopupMenu.java:887)
at javax.swing.plaf.basic.BasicComboPopup.show(BasicComboPopup.java:191)
at javax.swing.plaf.basic.BasicComboBoxUI.setPopupVisible(BasicComboBoxUI.java:859)
at javax.swing.JComboBox.setPopupVisible(JComboBox.java:796)
at com.mbs.generic.view.swing.combobox.AutoCompleteComboBox$1.focusGained(AutoCompleteComboBox.java:185)
at java.awt.AWTEventMulticaster.focusGained(AWTEventMulticaster.java:203)
at java.awt.Component.processFocusEvent(Component.java:6179)
at java.awt.Component.processEvent(Component.java:6046)
at java.awt.Container.processEvent(Container.java:2039)
at java.awt.Component.dispatchEventImpl(Component.java:4653)
at java.awt.Container.dispatchEventImpl(Container.java:2097)
at java.awt.Component.dispatchEvent(Component.java:4481)
at java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1848)
at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:901)
at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:513)
at java.awt.Component.dispatchEventImpl(Component.java:4525)
at java.awt.Container.dispatchEventImpl(Container.java:2097)
at java.awt.Component.dispatchEvent(Component.java:4481)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:648)
at java.awt.EventQueue.access$000(EventQueue.java:84)
at java.awt.EventQueue$1.run(EventQueue.java:607)
at java.awt.EventQueue$1.run(EventQueue.java:605)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:98)
at java.awt.EventQueue$2.run(EventQueue.java:621)
at java.awt.EventQueue$2.run(EventQueue.java:619)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:618)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
谢谢
首先,让我解释一下comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
的作用。通常,将鼠标悬停在项目上或按键盘上的箭头键将导致立即选择JComboBox
上的项目。由于来自JComboBox
的选择事件将导致单元格编辑过程停止,因此此行为不适用于表格单元格。因此,在设置此特殊客户端属性项时,将在弹出列表中显示为选中状态,但尚未在JComboBox
上设置。只有提交的项目(通过单击或 Enter 键(才会更改JComboBox
上的选定项目,从而导致编辑结束。至少,这适用于BasicLookAndFeel
及其衍生品。
您遇到的问题完全不同。正如异常消息和堆栈跟踪清楚地表明的那样,外观尝试打开与JComboBox
关联的JPopupMenu
(按照您的要求(,但它无法确定弹出菜单的屏幕位置,因为您的JComboBox
不是在屏幕上显示的。它想要JComboBox
位置的原因是它相对于JComboBox
打开了新窗口。
剩下的问题是,为什么您收到来自屏幕上未显示JComboBox
的focusGained
(或者您为什么认为您这样做了(。
像 JComboBox 中的下拉这样的弹出窗口往往具有事件处理顺序的边缘情况,因为它们没有在组件层次结构中的祖先中几何嵌套。 在您的情况下,您会导致框的焦点处理程序显示下拉列表。 为此,它需要该框已经位于屏幕上,但事实并非如此。
解决方案几乎可以肯定是推迟显示下拉列表,直到处理完使框可见的所有事件。 我有一个类似的(虽然不完全相同(的问题,并且能够以这种方式解决它。 令人高兴的是,有一个 Swing 实用程序函数可以解决问题。尝试将焦点获得处理程序的主体包装在invokeLater
和Runnable
中:
void focusGained() {
SwingUtilities.invokeLater(new Runnable() {
... focus gained body including show of pulldown menu here ...
});
}
invokeLater
将包含Runnable
的新消息放在队列的末尾,即 毕竟是现有的。 只有在处理完所有其他消息后,当消息到达头部时,才会执行该Runnable
。 这正是您想要的。
我第二(第三?第四?(每个人都要求使用您的自定义组合框的表格的精简示例,任何可能来自组合框本身的代码,但无论如何只是为了尝试一下......您是否尝试过制作自定义版本的 EditorDelegate 以配合您的其他自定义代码,并将用于显示弹出窗口的代码从focusGained()
移动到委托的 startCellEditing()
方法中?
如果你把你的指令嵌入到一个try.. catch指令中,你的程序将毫无问题地运行:
SwingUtilities.invokeLater(new Runnable(){
public void run()
{
try {
tInput.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
tInput.showPopup();
}
catch (IllegalComponentStateException e) {
return;
}
}
});