2017-01-06 4 views
4

Я программирование JToggleButton для загрузки в/выбросить из памяти конфигурации элемента (телескоп конфигурации), поэтому я добавил JComboBox в JFrame и рядом с ним кнопкой для загрузки выбранного элемента. Когда выбрано JToggleButton, отображается значок жесткого диска, другой значок, если в противном случае. Для этого я использую редактор IntelliJ IDEA GUI. Конечно, я добавил ItemListener (как это было предложено из Интернета) на эту кнопку:JToggleButton addItemListener кажется повторить ItemListener навсегда

loadTelescopeButton.setSelected(true); 
    System.out.println(loadTelescopeButton.isSelected()); 
    loadTelescopeButton.addItemListener(new ItemListener() { 
     @Override 
     public void itemStateChanged(ItemEvent e) { 
      System.out.println("LAODACTION " + loadTelescopeButton.isSelected()); 
      try { 
       if (e.getStateChange() == ItemEvent.SELECTED) { 
        String selected = telescopesList.getSelectedItem().toString(); 

        if ((selected != null) && (!selected.equals("")) && (ObjUtils.isAlphaNumeric(selected))) { 
         //... 

        } else { 
         showErrorMessage("Invalid id selected!"); 
        } 

       } else if (e.getStateChange() == ItemEvent.DESELECTED) { 
        if ((configurationActivity != null) && (configurationActivity.getManager() != null) && 
          (configurationActivity.getTelescope() != null) && (configurationActivity.getTelescope().isConnected())) { 
         //... 

        } else { 
         //... 
        } 
       } 

      } catch (Exception e1) { 
       e1.printStackTrace(); 
      } 
     } 
    }); 

Выход:
true
-> Когда окно отображается
LAOD_ACTION false
-> Когда я нажмите кнопку

Я провел несколько тестов с некоторыми новыми переключателями, и они дали мне такую ​​же ошибку: код внутри itemStateChanged(ItemEvent e) {...} повторяется навсегда, без stoppi нг! В этом фрагменте кода нет for и while петель! Результатом является большое количество диалогов сообщений (только один диалог должен отображаться), и если я фокусирую другое окно на своем рабочем столе, экран за окнами становится черным (область родительского окна). Я изменил слушателя на ActionListener, и теперь все выполняется один раз/клик.

Зачем нужна эта ошибка? Я скопировал этот код с https://stackoverflow.com/a/7524627/6267019, как вы можете видеть.

Полный код на GitHub Here, я выделил код для этой кнопки переключения. Такая же ошибка возникает и с другими JToggleButton s в моем MainActivity.java файле, а также при отладке IntelliJ позволяет мне видеть, что код в слушателе повторяется навсегда. После нескольких тысяч диалоговых окон Windows показывает мне сообщение и закрывает Java Platform Binary с ошибкой.

EDIT:
Та же проблема в новом классе:

import javax.swing.*; 
import java.awt.*; 

public class ErrorGUI extends JFrame { 

    public ErrorGUI() throws HeadlessException { 
     super("ciao"); 
     JPanel panel1 = new JPanel(); 
     setContentPane(panel1); 

     JToggleButton ciaoToggleButton = new JToggleButton("cajs"); 
     ciaoToggleButton.setSelected(true); 
     ciaoToggleButton.addItemListener(e -> { 
      System.out.println("caiooasfsdvn"); 
      try { 
       JOptionPane.showMessageDialog(panel1, "skjngksfnb"); 

      } catch (Exception e2) { 
       e2.printStackTrace(); 
      } 
     }); 
     panel1.add(ciaoToggleButton); 

     pack(); 
     setVisible(true); 
    } 

    public static void main(String[] args) { 
     new ErrorGUI(); 
    } 
} 
+0

Я знаю этот факт, и я сожалею об этом, но как я могу опубликовать 1200 строк кода или XML конструктора GUI IntelliJ IDEA? Какой пример я могу предоставить, если примеры из сети работают хорошо? – SquareBoot

+0

В этом проблема !! Примеры работают! Если я создаю новый проект, я добавляю 'main', и я пишу код для' JFrame' с помощью кнопки переключения, он работает! – SquareBoot

+0

Мое представление об ошибке заключается в том, что она вызвана внутренней ошибкой встроенного конструктора GUI IntelliJ IDEA. Теперь я создаю ту же ошибку в новом prj. – SquareBoot

ответ

3

Всякий раз, когда вы открываете модальный диалог, вызов метода открытия будет возвращать только после того, как диалог был закрыто. Это важно для диалоговых окон, которые возвращают введенное значение или выбор.

Это означает, что, когда диалог открыт, необходимо запустить новый цикл обработки событий, чтобы реагировать на ввод в диалоговом окне.

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

вложенной обработки событий может быть легко продемонстрирован путем изменений слушателя

ciaoToggleButton.addItemListener(e -> { 
    System.out.println("entering"); 
    JOptionPane.showMessageDialog(panel1, 
     e.getStateChange()==ItemEvent.SELECTED? "selected": "deselected"); 
    System.out.println("leaving"); 
}); 

, который будет печатать последовательности

entering 
entering 
leaving 
leaving 

, показывающие, как генерируется событие, противоречащее в то время как обработка старого не завершено.

По словам других, вы можете исправить это, открыв диалоговое после завершения четной обработки, как

ciaoToggleButton.addItemListener(e -> { 
    System.out.println("entering"); 
    EventQueue.invokeLater(() -> JOptionPane.showMessageDialog(panel1, 
     e.getStateChange()==ItemEvent.SELECTED? "selected": "deselected")); 
    System.out.println("leaving"); 
}); 

или применять немодальный диалог:

ciaoToggleButton.addItemListener(e -> { 
    System.out.println("entering"); 
    JDialog d = new JOptionPane(
      e.getStateChange()==ItemEvent.SELECTED? "selected": "deselected", 
      JOptionPane.INFORMATION_MESSAGE) 
     .createDialog(panel1, UIManager.getString("OptionPane.messageDialogTitle")); 
    d.setModal(false); 
    d.setVisible(true); 
    System.out.println("leaving"); 
}); 

(в реальном приложении вы либо сохраните диалог для последующего повторного использования, либо позвоните по телефону dispose после использования)


К сожалению, опасность открытия модальных диалогов (или выполнение чего-либо еще, что создает secondary event loop) недостаточно указана в документации. Вы можете читать везде, что доступ к компонентам Swing из других потоков может создавать несогласованности, но запуск нового цикла обработки событий, пока есть неполностью обработанные события, может иметь аналогичное влияние.

+0

Обратите внимание, что в исходном коде вы максимизируете это, вызывая 'setSelected (...);' в источнике событий из прослушивателя, что, конечно же, приведет к запуску новой доставки событий, сообщающей об изменениях прямо в этот момент, который является рецептом для бесконечных циклов, даже без диалогов. – Holger

+0

Спасибо за ваше время и объяснение. 1+. 10+, если бы я мог –

+0

Вау, это интересно! Я изучу этот вопрос позже, спасибо за ваш ответ! – SquareBoot

3

Я не могу сказать, что я понимаю, почему ваш код плохо себя, но я согласен, что вы видите, не совсем имеют смысл и, вероятно, из-за вызова JOptionPane каким-то образом влияют на изменение состояния JToggleButton. Один из способов обойти это - обернуть вызов JOptionPane в Runnable и выполнить его очередь в очереди событий Swing через SwingUtilities.invokeLater(...). Например:

import javax.swing.*; 
import java.awt.*; 

@SuppressWarnings("serial") 
public class ErrorGUI extends JFrame { 

    public ErrorGUI() throws HeadlessException { 
     super("ciao"); 
     JPanel panel1 = new JPanel(); 
     setContentPane(panel1); 

     JToggleButton ciaoToggleButton = new JToggleButton("cajs"); 
     ciaoToggleButton.setSelected(true); 
     ciaoToggleButton.addItemListener(e -> { 
      System.out.println("caiooasfsdvn"); 
      SwingUtilities.invokeLater(() -> { 
       JOptionPane.showMessageDialog(panel1, "skjngksfnb"); 
      }); 
      // JOptionPane.showMessageDialog(panel1, "skjngksfnb"); 

     }); 
     panel1.add(ciaoToggleButton); 

     pack(); 
     setDefaultCloseOperation(EXIT_ON_CLOSE); 
     setLocationRelativeTo(null); 
     setVisible(true); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(() -> { 
      new ErrorGUI(); 
     }); 

    } 
} 

Интересный вариант:

ciaoToggleButton.setSelected(true); 
System.out.println("0:" + ciaoToggleButton.isSelected()); 
ciaoToggleButton.addItemListener(e -> { 
    System.out.println("1: " + ciaoToggleButton.isSelected()); 
    if (e.getStateChange() == ItemEvent.SELECTED) { 
     JOptionPane.showMessageDialog(panel1, "skjngksfnb"); 
    } 
    System.out.println("2: " + ciaoToggleButton.isSelected()); 

}); 

печатает:

0:true 
1: false 
2: false 
1: true 
1: false 
2: false 
2: false 
1: true 
1: false 
2: false 
2: false 
+0

Спасибо, но вот еще одна странная вещь: я написал 'loadTelescopeButton.setSelected (true); System.out.println (loadTelescopeButton.isSelected()); loadTelescopeButton.addActionListener (e -> { System.out.println ("LAOD_ACTION" + loadTelescopeButton.isSelected()); // ... другой код' в конструкторе. В консоли я вижу 'LAOD_ACTION false', когда я нажмите кнопку! – SquareBoot

+0

@SquareBoot: пожалуйста, отредактируйте свой вопрос. Код в комментариях трудно прочитать, если он не очень мал. –

+0

На самом деле спасибо за ответ! Итак, похоже, это ошибка Java, на мой взгляд. тратя так много времени на кнопку переключения, что я собираюсь создать пользовательский 'JComponent' – SquareBoot

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