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);

Это, однако, не помогает. Что это должно делать в любом случае?

Все темы и статьи, которые я читал об этом, очень расплывчаты в отношении природы проблемы.

Есть ли у кого-нибудь понимание природы, почему эта проблема возникает? Мой комбинированный список очень индивидуален, поэтому он поможет понять суть проблемы, чтобы я мог исправить код.

Это вызвано событием, полученным в фокусе, в поле со списком, которое захватывается и вызывает 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 ответа

Во-первых, позвольте мне объяснить, что comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); делает. Обычно при наведении курсора мыши на элемент или нажатии клавиш со стрелками на клавиатуре элементы выбираются на JComboBox немедленно. С момента выбора событий из JComboBox приведет к остановке процесса редактирования ячейки, это поведение не подходит для ячеек таблицы. Таким образом, при установке этого специального клиента свойства элементов будут отображаться выбранными внутри всплывающего списка, но не установлены в JComboBox еще. Только подтвержденные элементы (с помощью щелчка или клавиши Enter) изменят выбранный элемент на JComboBox вызывая конец редактирования тогда. По крайней мере, это верно для BasicLookAndFeel и его производные.

Проблема у вас совершенно другая. Как четко говорится в сообщении об исключении и трассировке стека, внешний вид пытается открыть JPopupMenu связано с JComboBox (как вы просили), но он не может определить местоположение на экране для всплывающего меню, потому что ваш JComboBox не выстрелил, показывая на экране. Причина, почему он хочет, чтобы местоположение JComboBox является то, что он открывает новое окно относительно JComboBox,

Остается вопрос, почему вы получили focusGained из JComboBox это не отображается на экране (или почему вы думали, что сделали).

Всплывающие окна, такие как раскрывающийся список в 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;
                                }

                          }
                 }); 
Другие вопросы по тегам