jcombobox作为单元格编辑器java.awt.IllegalComponentStateException:必须在屏幕上显示组件以确定其位置

我使用自定义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); 

但这并没有帮助。 无论如何这应该做什么?

我读过的关于这个的所有主题和文章对于问题的本质都非常模糊。

有没有人对这个问题发生的原因有什么了解? 我的combobox是非常自定义的,所以它有助于理解问题的基础,所以我可以修复代码。

这是在捕获的combobox上的焦点获取事件上触发并调用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); 确实。 通常,将鼠标hover在某个项目上或按下键盘上的箭头键将立即导致选择JComboBox上的项目。 由于来自JComboBox选择事件将导致单元格编辑过程停止,因此此行为不适用于表格单元格。 因此,当设置此特殊客户端属性项时,将在弹出列表中显示选中但尚未在JComboBox上设置。 只有提交的项目(通过单击或Enter键)将更改JComboBox上的选定项目,然后导致编辑结束。 至少,这适用于BasicLookAndFeel及其衍生产品。

你遇到的问题完全不同。 由于exception消息和堆栈跟踪清楚地说明,外观尝试打开与JComboBox关联的JPopupMenu (如您所请求的)但它无法确定弹出菜单的屏幕位置,因为您的JComboBox未被拍摄显示在屏幕上。 它想要JComboBox的位置的原因是它打开了相对于JComboBox的新窗口。

剩下的问题是为什么你从一个没有在屏幕上显示的JComboBox (或者你认为你做过的原因)收到了一个focusGained

类似于JComboBox中的下拉列表的弹出窗口往往具有事件处理顺序的边缘情况,因为它们在几何上没有嵌套在组件层次结构中的祖先中。 在您的情况下,您正在使框的焦点处理程序显示下拉。 要做到这一点,它需要盒子已经位于屏幕上,但事实并非如此。

解决方案几乎肯定是推迟显示下拉,直到所有可以使框可见的事件都被处理完毕。 我有一个类似的(虽然不完全相同)问题,并能够以这种方式解决它。 令人高兴的是,有一个Swing实用程序function可以解决问题。 尝试在invokeLaterRunnable包装焦点获取处理程序的主体:

 void focusGained() { SwingUtilities.invokeLater(new Runnable() { ... focus gained body including show of pulldown menu here ... }); } 

invokeLater在队列末尾放置一条包含Runnable新消息,即在所有现有消息之后 。 只有在消息处理完所有其他消息后,才会执行Runnable 。 这正是你想要的。

我是第二个(第三个?第四个?)每个人都要求使用你的自定义combobox的一个表的简化示例任何可能是combobox本身的一些代码,但无论如何只是为了刺它…你试过吗?制作一个自定义版本的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; } } });