2015-08-19 5 views
1

У меня есть приложение, где, если действие пользователя занимает определенное время, отображается диалоговое окно, в котором интерфейс обрабатывает действие. Обычно, когда действие выполняется, диалог закрывается, и пользователь может продолжать делать то, что он делал раньше. Кажется, что это работает почти всегда, но время от времени фокус перемещается из компонента, который был сфокусирован на компонент, который находится в 2 местах после него в цепочке фокуса.Неверное поле сфокусировано после закрытия диалога

Я не могу создать небольшую примерную программу, чтобы показать, что именно происходит, но я смог отладить проблему до такой степени, что я надеюсь, что кто-то может мне помочь.

Что, кажется, происходит это:

  1. sun.awt.TimedWindowEvent [WINDOW_GAINED_FOCUS, {главное окно} ...] событие обрабатывается в диалоге и передается на DefaultKeyboardFocusManager (DKFM), который попытается восстановить фокус в главное окно.
  2. DKFM говорит MostRecentFocusOwner сосредоточить себя
  3. Компонент регистрирует себя как MostRecentFocusOwner и пытается на самом деле сосредоточиться на себя, вызвав peer.requestFocus.
  4. * В WComponentPeer, кадр сфокусирован, но по какой-то причине он возвращает ложь
  5. компонент видит не фокусировалось, так что возвращает ложь, а
  6. DKFM видит, что компонент не был сосредоточены так он пытается сосредоточиться следующий компонент
  7. Шаг 3-5
  8. Еще одно событие вызывает фокус, шаг 1-7 запуска снова, но потому, что шаг 3 регистров нового компонента в MostRecentFocusOwner цикл переходит компонента из 6 и следующий оттуда
  9. Еще одно событие фокусировки запускает логику с шага 1, но теперь шаг 4 возвращает true. Отобразится главное окно, и теперь будет сфокусирован неправильный компонент.

Для 4 *: Дело в том, что, как представляется, отличается в тех ситуациях, где он работает и где она не в том, что проверяет WComponentPeer после запроса фокусировки для ParentWindow где в правильных ситуациях parentWindow.isFocused() истинно, и в неудачных ситуациях parentWindow.isFocused() вернет false. Запись, отображаемая при включении регистрации фокуса, - «rejectFocusRequestHelper [...] Ожидание асинхронной обработки запроса».
Это, по-видимому, указывает на то, что 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 обрабатывать определенные запросы фокуса асинхронно и другие синхронно?

Редактировать: Это может быть событие заказа событий. Глядя на focusedWindow в KeyboardFocusManager в правильной ситуации это выглядит следующим образом:

  1. Основной каркас -> нулевой
  2. Null -> Диалог Диалог
  3. изменения фокуса (я ожидаю диалога)
  4. -> нулевой
  5. нуль -> Главный кадр
  6. Фокус изменение (в lastFocussedComponent

В неисправной ситуации это так:

  1. Основного каркас -> нулевого
  2. 4x Фокусное изменение (к следующему фокусируемой компоненте на основной раме)
  3. нуля -> Главного кадра

Кажется, что диалог удален до того, как он будет показан. Событие, которое очищает основной кадр в качестве активного окна, в обоих случаях имеет WINDOW_FOCUS_LOST TimedWindowEvent с диалогом, противоположным.

ответ

0

Из того, что я могу сказать, теперь это вызвано ошибкой 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 перед закрытием диалогового окна помогает предотвратить ошибку.

Смежные вопросы