4

我在 JTable 中使用自定义 JComboBox 作为单元格编辑器。当用户使用键盘控件进入单元格时,它会尝试打开弹出窗口。这会导致以下错误:

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)

谢谢

4

4 回答 4

5

首先,让我解释一下是做什么comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);的。通常,将鼠标悬停在某个项目上或按键盘上的箭头键会JComboBox立即导致选择项目。由于来自 的选择事件JComboBox将导致单元格编辑过程停止,因此此行为不适用于表格单元格。因此,当设置此特殊客户端属性时,将在弹出列表中显示选中但尚未设置JComboBox。只有已提交的项目(通过单击或 Enter 键)才会在JComboBox导致编辑结束时更改所选项目。至少,这适用于BasicLookAndFeel它的衍生产品。

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

剩下的问题是为什么你收到了focusGained一个JComboBox没有在屏幕上显示的邮件(或者你为什么认为你这样做了)。

于 2013-09-26T16:12:12.763 回答
1

像 JComboBox 中的下拉菜单这样的弹出窗口往往具有事件处理顺序的边缘情况,因为它们没有在几何上嵌套在组件层次结构中的祖先内部。在您的情况下,您正在导致框的焦点处理程序显示下拉。为此,它需要该框已经位于屏幕上,但事实并非如此。

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

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

invokeLater包含 的新消息放在Runnable队列末尾,即 在所有现有消息之后。仅当Runnable消息到达头部时才会执行,在所有其他消息都已处理之后。这正是你想要的。

于 2013-09-28T23:33:54.650 回答
0

我第二(第三?第四?)每个人都要求使用您的自定义组合框的表格的精简示例,任何可能来自组合框本身的代码,但只是为了尝试一下......你试过了吗制作自定义版本的EditorDelegate以与您的其他自定义代码一起使用并将用于显示弹出窗口的代码移动focusGained()到您的委托startCellEditing()方法中?

于 2013-10-02T23:29:04.563 回答
-2

如果您将指令嵌入到 try .. catch 指令中,您的程序将毫无问题地运行:

SwingUtilities.invokeLater(new Runnable(){

                        public void run()
                        {
                        try {
                        tInput.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
                        tInput.showPopup();
                        }
                        catch   (IllegalComponentStateException e) {
                                return;
                                }

                          }
                 }); 
于 2015-02-17T10:55:24.133 回答