Element aus comboBox mit observableList löschen

WSchubert

Neues Mitglied
Guten Tag Forum!

Ich beschäftige mich gerade mit einem JavaFX-Projekt und bin Anfänger.
Folgendes Ziel habe ich:
In der comboBoxLK1 soll aus der observableList "items" ein Element ausgewählt werden. Dieses gewählte Element soll dann aus der observableList "items" entfernt werden. Dann soll die comboBoxLK1 deaktiviert werden. Dann soll die comboBoxLK2 mit der aktualisierten observableList "items" aktiviert werden, um dann auch hier ein Element aus "items" zu wählen. Dies soll dann noch für weitere comboBoxen so durchgeführt werden.
Ich möchte also aus der observableList "items" kein fixes Element löschen, sondern das Element, das in der ComboBox gewählt wurde.
Dies scheint nicht richtig zu funktionieren. Mein Eindruck ist, dass beim Löschen die setonAction für die comboBox nochmals durchgeführt wird. In Schleife sozusagen.

Hier der Code:

Java:
ComboBox<String> comboBoxLK1 = new ComboBox<>();
         ObservableList<String> items = FXCollections.observableArrayList();
         items.addAll("Rot", "Gelb", "Grün", "Blau", "Schwarz", "Lila");
        
         comboBoxLK1.setItems(items);
        
         ComboBox<String> comboBoxLK2 = new ComboBox<>();
        
         comboBoxLK2.setDisable(true);
        
         comboBoxLK1.setOnAction((event) -> {
             int selectionIndexLK1 = comboBoxLK1.getSelectionModel().getSelectedIndex();
             Object selectionItemLK1 = comboBoxLK1.getSelectionModel().getSelectedItem();
             System.out.println("Selection made 1: [" + selectionIndexLK1 + "] " + selectionItemLK1);
             System.out.println(items);
             comboBoxLK1.setDisable(true);
            
             items.remove(selectionIndexLK1);
             //items.remove("Grün");             
             System.out.println(items);
             comboBoxLK1.setDisable(true);
             comboBoxLK2.setItems(items);
             comboBoxLK2.setDisable(false);
                    
         });
        
        
         comboBoxLK2.setOnAction((event) -> {
             int selectionIndexLK2 = comboBoxLK2.getSelectionModel().getSelectedIndex();
             Object selectionItemLK2 = comboBoxLK2.getSelectionModel().getSelectedItem();
            
             System.out.println("Selection made 2: [" + selectionIndexLK2 + "] " + selectionItemLK2);
             System.out.println(items);
            
             items.remove(selectionItemLK2);
             //items.remove("Schwarz");
             System.out.println(items);
             comboBoxLK2.setDisable(true);
            
             //comboBoxLK3.setItems(items);
             //comboBoxLK3.setDisable(false);
                    
         });

Hier die Ausgabe der println auf der Console:
Code:
Selection made 1: [0] Rot
[Rot, Gelb, Grün, Blau, Schwarz, Lila]
Selection made 1: [-1] null
[Gelb, Grün, Blau, Schwarz, Lila]
Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 5
    at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
    at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
    at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
    at java.base/java.util.Objects.checkIndex(Objects.java:359)
    at java.base/java.util.ArrayList.remove(ArrayList.java:504)
    at javafx.base@19/com.sun.javafx.collections.ObservableListWrapper.doRemove(ObservableListWrapper.java:116)
    at javafx.base@19/javafx.collections.ModifiableObservableListBase.remove(ModifiableObservableListBase.java:186)
    at FirstJavaFXProject/application.Kurswahl.lambda$0(Kurswahl.java:40)
    at javafx.base@19/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base@19/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    at javafx.base@19/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)

In der Console wird zunächst richtig ausgeben, dass die Farbe Rot gewählt wurde:
Selection made 1: [0] Rot
Dann wird nochmals die observablelist "items" ausgegeben:
[Rot, Gelb, Grün, Blau, Schwarz, Lila]

Dann aber scheint die setonAction-Routine nochmals zu starten, ohne dass ich in der Liste klicke, denn es wird nochmals die println ausgeführt:
Selection made 1: [-1] null

Und dann sieht man auch, dass das Löschen der zuvor gewählten Farbe (hier "Rot") auch über die Variable 'selectionIndexLK1' funktioniert, denn nun zeigt die println folgende Liste:
[Gelb, Grün, Blau, Schwarz, Lila]

Dann kommen die Fehlermeldungen mit ExceptionInThread...

Kann sich jemand dieses Verhalten erklären?

Wenn ich ein Element direkt aus der 'items'-Liste lösche (Zeilen 41 und 58 im Code aktiviert und Zeilen 40 und 57 deaktiviert), dann funktioniert es reibungslos ohne Fehlermeldungen. Aber ich will ja keine festen Elemente aus der Liste löschen sondern die zuvor gewählten.

Vielen Dank!
 
Also erst einmal würde ich nicht wirklich auf Actions reagieren - zumal da ja deutlich mehr Actions kommen - erkennbar an dem Parameter, den Du komplett ignorierst. (Merke: Wenn Du einen Parameter komplett ignorierst, dann ist dies meist ein Punkt, den man sich nächer anschauen sollte. Bei So UI Entwicklung mag es vorkommen, aber das ist nichts, das ich so gut finden würde.)

Aber dir geht es ja auch um die Auswahl eines Elementes und nicht generell um Actions. Daher macht es hier aus meiner Sicht Sinn, das SelectionModel zu nutzen. Da kann man z.B. die selectedProperty nutzen, um mitzubekommen, ob sich ein ausgewähltes Element ändert.

Dann ist es schlecht, die Liste zu verändern - dann dürfte das ausgewählte Element sich ja wieder ändern (Etwas, das evtl. das Kernproblem ist bei Dir!). Eine Observable List würde ich da also nicht verwenden um dieses dann an mehrere ComboBoxen zu binden. Da würde ich jedem eine eigene Liste geben und diese dann entsprechend füllen wenn es soweit ist. Dieses "Ich benutze eine Instanz an vielen Stellen für unterschiedliche Dinge" ist nicht wirklich sauber und kann durchaus zu Problemen führen.

Wenn Du das aber so beibehalten willst: Dann kannst Du auch in dem Event abprüfen, ob das Control noch aktiv ist. Wenn das Control nicht mehr aktiv ist, dann behandelst Du das Event nicht mehr. Das könnte sogar auch bei dir im Code schon ausreichen.

Java:
        comboBoxLK1.setOnAction((event) -> {
            if (comboBoxLK1.isDisabled()) return;

             int selectionIndexLK1 = comboBoxLK1.getSelectionModel().getSelectedIndex();
             Object selectionItemLK1 = comboBoxLK1.getSelectionModel().getSelectedItem();
             comboBoxLK1.setDisable(true);
             System.out.println("Selection made 1: [" + selectionIndexLK1 + "] " + selectionItemLK1);
             System.out.println(items);
            
             items.remove(selectionIndexLK1);
             //items.remove("Grün");             
             System.out.println(items);
             comboBoxLK1.setDisable(true);
             comboBoxLK2.setItems(items);
             comboBoxLK2.setDisable(false);
                    
         });

Zu guter letzt: Schön, dass Du da wenigstens keine anonymen Klassen verwendet hast, aber so Lambda Expressions über mehrere Zeilen sollte man nicht nutzen, da diese schlicht zu unübersichtlich sind. Packe es statt dessen in eine Methode und dann setzt Du nur eine Methodenreferenz. Oder da Du ja mehrfach den gleichen Code hast: Nutze Parameter! Dann hättest Du etwas wie:

Java:
        comboBoxLK1.setOnAction( event -> handleComboBoxClick(comboBoxLK1, comboBoxLK2) );


    public void handleComboBoxClick(ComboBox mainBox, ComboBox followingBox) {
        if (mainBox.isDisabled()) return;
        
        int selectedIndex = mainBox.getSelectionModel().getSelectedIndex();
        Object selectedItem = mainBox.getSelectionModel().getSelectedItem();
        mainBox.setDisable(true);
        items.remove(selectedIndex);
        followingBox.setItems(items);
        followinBox.setDisable(false);
    }

Aber wie gesagt: So würde ich es normalerweise nicht machen.
 
Hallo KonradN,
vielen Dank für die ausführliche Rückmeldung!
Die verschiedenen Alternativen werde ich mal versuchen umzusetzen.
Das Einfügen der Zeile "if (comboBoxLK1.isDisabled()) return;" hat aber schon perfekt funktioniert. Jetzt läuft der Ablauf ohne Fehler durch!
Super!
Dann wäre jetzt natürlich die Frage, wo genau in der Programmausführung der Unterschied besteht zwischen "items.remove(selectionIndexLK1);" und "items.remove("Grün");"? Der eine gibt Fehler, der andere nicht.

Hintergedanke der gemeinsamen Liste war, dass das Programm kaskadenartig verschiedene ComBoxen durchlaufen soll und in jeder folgenden Box nur noch die Elemente übrig sein sollen, die in den vorherigen Boxen noch nicht gewählt wurden.

Danke nochmals!
 
Dann wäre jetzt natürlich die Frage, wo genau in der Programmausführung der Unterschied besteht zwischen "items.remove(selectionIndexLK1);" und "items.remove("Grün");"? Der eine gibt Fehler, der andere nicht.
Ich habe mir das nicht im Detail angesehen, aber vermutlich wird da folgendes passieren:
  • Wenn Du das aktuelle ausgewählte Element löschst, dann ändert sich die Selektion (Es ist dann nichts mehr selektiert).
  • Dadurch wird wieder ein Action Event ausgelöst
  • In dem neuen Action Event ist ja nichts mehr selektiert und die Abfrage nach dem selected Index wird vermutlich -1 zurück geben.
  • Du versuchst dann das Elemen -1 zu löschen und das geht natürlich nicht!
 

Neue Themen


Zurück
Oben