2017-01-06 3 views
4

Ich programmiere ein JToggleButton zu laden/aus dem Speicher zu verwerfen, die die Konfiguration eines Elements (ein Teleskop config), also habe ich eine JComboBox in einem JFrame und in unmittelbarer Umgebung der Schaltfläche hinzugefügt um das ausgewählte Objekt zu laden. Wenn JToggleButton ausgewählt ist, wird ein Festplattensymbol angezeigt, andernfalls ein anderes Symbol. Ich benutze dafür den IntelliJ IDEA GUI Editor. Natürlich habe ich hinzugefügt eine ItemListener (wie von der Bahn vorgeschlagen) auf diese Schaltfläche:JToggleButton addItemListener scheint die ItemListener zu wiederholen immer

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

Ausgang:
true
- ist> Wenn das Fenster
angezeigt LAOD_ACTION false
-> Als ich Klicken Sie auf die Schaltfläche

Ich habe einige Tests mit einigen neuen Umschalttasten gemacht und sie gaben mir den gleichen Fehler: Der Code innerhalb itemStateChanged(ItemEvent e) {...} wird für immer wiederholt, ohne stoppi ng! In diesem Codeabschnitt gibt es keine for und while Schleifen! Das Ergebnis ist eine große Anzahl von Meldungsdialogen (nur ein Dialog sollte angezeigt werden), und wenn ich ein anderes Fenster in meinem Desktop fokussiere, wird der Bildschirm hinter den Dialogen schwarz (der Bereich des Elternfensters). Ich habe den Listener zu ActionListener geändert und jetzt wird alles einmal/Klick ausgeführt.

Warum dieser Fehler? Ich habe diesen Code von https://stackoverflow.com/a/7524627/6267019 kopiert, wie Sie sehen können.

Voller Code auf GitHub Here, Ich habe den Code für diese Umschaltfläche hervorgehoben. Der gleiche Fehler passiert mit anderen JToggleButton s in meiner MainActivity.java Datei, und auch beim Debugging IntelliJ lässt mich sehen, dass der Code im Hörer für immer wiederholt wird. Nach einigen tausend Dialogen zeigt Windows mir eine Nachricht an und schließt Java Platform Binary mit einem Fehler.

EDIT:
Das gleiche Problem in einer neuen Klasse:

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

Ich weiß diese Tatsache und es tut mir leid, aber wie kann ich 1200 Zeilen Code oder das XML des IntelliJ IDEA GUI Builders veröffentlichen? Welches Beispiel kann ich anbieten, wenn die Beispiele aus dem Web gut laufen? – SquareBoot

+0

Das ist das Problem !! Die Beispiele funktionieren! Wenn ich ein neues Projekt erstelle, füge ich ein 'main' hinzu und schreibe den Code für' JFrame' mit einem Toggle-Button, es funktioniert! – SquareBoot

+0

Meine Idee des Fehlers ist, dass es durch einen internen IntelliJ IDEA GUI Builder Fehler verursacht wird. Jetzt erstelle ich den gleichen Fehler in einem neuen Prj. – SquareBoot

Antwort

3

Jedes Mal, wenn Sie einen modalen Dialog öffnen, wird die Öffnung Methodenaufruf nur zurückkehren, nachdem der Dialog war geschlossen. Dies ist entscheidend für die Dialoge, die einen eingegebenen Wert oder eine Auswahl zurückgeben.

Dies bedeutet, dass während des Öffnens des Dialogs eine neue Event-Handling-Schleife gestartet werden muss, um auf die Eingabe im Dialog zu reagieren.

Wenn Sie also einen modalen Dialog von einem Listener öffnen, stoppen Sie die Behandlung des aktuellen Ereignisses und starten die Verarbeitung nachfolgender Ereignisse, was die Behandlung des aktuellen Ereignisses erheblich beeinträchtigen kann.Vor allem verliert die Schaltfläche plötzlich den Fokus, wenn das neue Dialogfeld geöffnet wird.

Die verschachtelte Ereignisbehandlung durch Ändern des Zuhörers

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

leicht nachgewiesen werden kann, welche Sequenzen von

entering 
entering 
leaving 
leaving 

zeigt, wie die widersprechenden Ereignisses während der Bearbeitung der alten erzeugt druckt ist nicht abgeschlossen.

Wie bereits von anderen gesagt, können Sie dieses Problem beheben Sie den Dialog nach die Fertigstellung der auch Handhabung, wie

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

oder Sie erzwingen einen nicht-modalen Dialog durch Öffnen:

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

(in einer realen Anwendung würden Sie entweder den Dialog zur späteren Wiederverwendung behalten oder dispose nach der Verwendung anrufen)


Leider ist die Gefahr des Öffnens modaler Dialoge (oder irgendetwas anderes, das eine secondary event loop erstellt) nicht genug in der Dokumentation hervorgehoben worden. Sie können überall lesen, dass der Zugriff auf Swing-Komponenten aus anderen Threads zu Inkonsistenzen führen kann, aber das Starten einer neuen Ereignisbehandlungsschleife bei unvollständig verarbeiteten Ereignissen ähnliche Auswirkungen haben kann.

+0

Beachten Sie, dass Sie in Ihrem ursprünglichen Code dies maximieren, indem Sie 'setSelected (...);' für die Ereignisquelle innerhalb des Listeners aufrufen, was natürlich eine neue Ereignislieferung auslösen wird, die die Änderung an dieser Stelle meldet. Das ist ein Rezept für Endlosschleifen, auch ohne Dialoge. – Holger

+0

Vielen Dank für Ihre Zeit und Erklärung. 1+. 10+ wenn ich könnte –

+0

Wow, das ist interessant! Ich werde diese Frage später studieren, danke für deine Antwort! – SquareBoot

3

Ich kann nicht sagen, dass ich, warum Ihr Code zu verstehen ist misbehaving, aber ich stimme zu, dass das tut, was Sie sehen nicht ganz macht Sinn und ist wahrscheinlich auf den JOptionPane-Aufruf zurückzuführen, der sich irgendwie auf die Statusänderung des JToggleButton auswirkt. Eine Möglichkeit, dies zu umgehen, besteht darin, den JOptionPane-Aufruf in ein Runnable-Objekt einzufügen und ihn in der Swing-Ereigniswarteschlange über SwingUtilities.invokeLater(...) einzureihen. Zum Beispiel:

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

    } 
} 

Eine interessante Variante:

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

}); 

druckt:

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

Danke, aber hier ist noch eine seltsame Sache: Ich habe 'loadTelescopeButton.setSelected (true) geschrieben; System.out.println (loadTelescopeButton.isSelected()); loadTelescopeButton.addActionListener (e -> { System.out.println ("LAOD_ACTION" + loadTelescopeButton.isSelected()); // ... anderer Code' im Konstruktor. In der Konsole sehe ich 'LAOD_ACTION false' wenn ich Klicke auf den Knopf! – SquareBoot

+0

@SquareBoot: Bitte ändere deine Frage Code in Kommentaren ist schwer zu lesen, es sei denn, es ist sehr klein –

+0

Wirklich danke für die Antwort! Also, es scheint ein Java-Bug zu sein, meiner Meinung nach so viel Zeit auf dem Schalter zu verbringen, dass ich eine benutzerdefinierte 'JComponent' machen werde – SquareBoot

Verwandte Themen