Dynamische JComboBox

Status
Nicht offen für weitere Antworten.

enriico

Mitglied
hallo zusammen

ich versuche eine ganz spezielle jcombobox zu entwickeln und zwar soll die jcombobox
beim eingeben eines buchstaben die einträge in der liste entsprechend filtern. dazu habe
ich eine lauffähige version entwickelt.

zur zeit besteht nur ein kleines problem und zwar sieht dieses wie folgt aus. wenn ich nun einen eintrag
mit der maus auswähle, wird dieser erfolgreich ins textfield geschrieben. wenn ich nun jedoch den eintrag erneut auswähle, wird der eintrag gelöscht. ich frage mich weshalb oder besser gesagt wie ich dieses problem lösen kann.

besten dank
fabio


Code:
import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;


/**
* Diese Klasse repräsentiert eine modifizierte JComboBox, welche bei der Eingabe von Zeichen die Auswahlliste
* dynamisch verkleinert oder vergrössert.
*
* @author Fabio Ferrari (FFE)
* @version 1.0
*/
public class MDynamicComboBox extends JComboBox {
/**
* Dieser Vector beinhaltet alle Elemente die in die JComboBox geladen werden sollen. Der Effektive
* Datenbestand ist im DataModel vorhanden.
*/
private Vector<String> originalValues;

/**
* Enthält alle Listener die sich für den SelectedElementPerformed Event registriert haben.
*/
private MDynamicComboBoxEventMulticaster multicaster = new MDynamicComboBoxEventMulticaster();

/**
* Fügt ein MDynamicComboBoxListener der Interessentenliste hinzu.
*
* @param l Referenz auf den Listener
*/
public void addMComboBoxListener(MDynamicComboBoxListener l) {
multicaster.add(l);
}

/**
* Entfernt einen MDynamicComboBoxListener aus der Interessentenliste.
*
* @param l Referenz auf den zu löschenden Listener
*/
public void removeMComboBoxListener(MDynamicComboBoxListener l) {
multicaster.remove(l);
}

/**
* Gibt die Anzahl der registrierten Listener zurück.
*
* @return Anzahl der registrierten Listener
*/
public int getMDynamicComboBoxListenerCount(){
return multicaster.getSize();
}

/**
* Default-Konstruktor der Klasse.
*
* @param model Als Parameter wird das Dynamische ComboBoxModel benötigt
*/
public MDynamicComboBox(MDynamicComboBoxModel model) {
super(model);

originalValues = (Vector<String>) model.getElements().clone();

JTextField tf = (JTextField) getEditor().getEditorComponent();
tf.setDocument(new MDynamicComboBoxDocument());
getEditor().addActionListener(new MDynamicSelectTextActionListener());
}

/**
* Diese Klasse enthält alle Listener die informiert werden wollen, wann ein Element in der ComboBox ausgewählt wurde.
*
* @author Fabio Ferrari (FFE)
* @version 1.0
*/
private class MDynamicComboBoxEventMulticaster implements MDynamicComboBoxListener {
/**
* Enthält alle Listener die sich für den SelectedElementPerformed Event registriert haben.
*/
private Vector listener = new Vector();

/**
* Informiert alle registrierten Listener darüber, dass ein Element ausgewählt wurde.
*
* @param evt Referenz auf den entsprechenden Event
*/
public void selectedElementPerformed(MDynamicComboBoxEvent evt) {
for (int i = 0; i < listener.size(); i++) {
((MDynamicComboBoxListener) listener.elementAt(i)).selectedElementPerformed(evt);
}
}

/* 1 eingefügt wurde (z.b. bei einem Mausklick im PopupMenu) und dieser String eine
* gültige Methode darstellt, dann sollen alle Listener darüber informiert werden.
*/
if (originalValues.contains(str)) {
multicaster.selectedElementPerformed(new MDynamicComboBoxEvent(this, str));
// hidePopup();
}
}

/**
* Entfernt ein Zeichen aus dem Eingabefeld
*
* @param offset Position im Eingabefeld
* @param len Anzahl der Zeichen
* @throws BadLocationException Falls ein Fehler beim Löschen auftaucht.
*/
public void remove(int offset, int len) throws BadLocationException {
super.remove(offset, len);
System.out.println(offset + ": " + len);
refresh();
}

/**
* Diese Methode aktualisiert die Einträge im Popup werden. Es werden entweder Elemente entfernt oder neu
* hinzugefügt.
*/
public void refresh() {
JTextField tf = (JTextField) getEditor().getEditorComponent();
String text = tf.getText();
MDynamicComboBoxModel originalModel = (MDynamicComboBoxModel) getModel();

originalModel.removeAllElements();
for (int i = 0; i < originalValues.size(); i++) {
if (originalValues.get(i).startsWith(text) || text.isEmpty()) {
originalModel.addElement(originalValues.get(i));
}
}

/* objects;
private Object selectedObject = null;

public MDynamicComboBoxModel(Vector<String> v) {
objects = v;
}

public void setSelectedItem(Object anObject) {
if ((selectedObject != null && !selectedObject.equals(anObject)) || selectedObject == null && anObject != null) {
selectedObject = anObject;
}
}

public Object getSelectedItem() {
return selectedObject;
}

public int getSize() {
return objects.size();
}

public Object getElementAt(int index) {
if (index >= 0 && index < objects.size()) {
return objects.elementAt(index);
} else {
return null;
}
}

public int getIndexOf(Object anObject) {
return objects.indexOf(anObject);
}

public Vector<String> getElements() {
return objects;
}

public void addElement(Object obj) {
objects.addElement(obj.toString());
}

public void insertElementAt(Object obj, int index) {
}

public void removeElementAt(int index) {
if ((index = 0)) {
if (getElementAt(index) == selectedObject) {
if (index == 0) {
setSelectedItem(getSize() == 1 ? null : getElementAt(index + 1));
} else {
setSelectedItem(getElementAt(index - 1));
}
}
objects.removeElementAt(index);
}
}

public void removeElement(Object anObject) {
int index = objects.indexOf(anObject);
if (index != -1) {
removeElementAt(index);
}
}

public void removeAllElements() {
if (objects.size() > 0) {
objects.removeAllElements();
}
selectedObject = null;
}

public boolean contains(String str){
return objects.contains(str);
}
}

##########################

import java.util.EventListener;

/**
* Dieser Listener wird aufgerufen, wenn Elemente in der ComboBox ausgewählt wurden.
* Dieser Listener hat grosse Parallelen mit dem ItemListener, doch werden bei diesem Listener keine überflüssigen
* Events weitergegeben.
*
* @author Fabio Ferrari (FFE)
* @version 1.0
*/
public interface MDynamicComboBoxListener extends EventListener {

/**
* Callback Methode wird aufgerufen, sobald ein Element selektiert wurde.
*
* @param evt Referenz auf den Event und das Element
*/
public void selectedElementPerformed(MDynamicComboBoxEvent evt);
}

#######################

import java.util.EventObject;

/**
* Diese Klasse repräsentiert einen Event der verteilt wird, wenn ein Element bei der ComboBox ausgewählt wurde.
*
* @author Fabio Ferrari (FFE)
* @version 1.0
*/
public class MDynamicComboBoxEvent extends EventObject {

/**
* Name des ausgewählten Elements
*/
private String selectedElement;

/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public MDynamicComboBoxEvent(Object source, String selectedElement) {
super(source);
this.selectedElement = selectedElement;
}

/**
* Gibt das markierte Element zurück.
*
* @return selectedElement Markierte Element
*/
public String getSelectedElement(){
return selectedElement;
}
}
 
oops da habe ich wohl nicht alle klassen mitkopiert. nun ist es jedoch komplett. ihr benötigt den oberen code nicht mehr zu berücksichtigen. nur noch dieser ist relevant. wie bereits gesagt, die combobox funktioniert soweit so gut. einzig wenn ihr ein element mit der maus aus der auswahlliste auswählt und anschliessend erneut auswählt, wird es im jtextfield gelöscht.

findet ihr noch andere fehler?

Code:
package Variante1;

import java.util.EventListener;

/**
 * Dieser Listener wird aufgerufen, wenn Elemente in der ComboBox ausgewählt wurden.
 * Dieser Listener hat grosse Parallelen mit dem ItemListener, doch werden bei diesem Listener keine überflüssigen
 * Events weitergegeben.
 *
 * @author Fabio Ferrari (FFE)
 * @version 1.0
 */
public interface Variante1MDynamicComboBoxListener extends EventListener {

    /**
     * Callback Methode wird aufgerufen, sobald ein Element selektiert wurde.
     *
     * @param evt Referenz auf den Event und das Element
     */
    public void selectedElementPerformed(Variante1MDynamicComboBoxEvent evt);
}
####################################################
Code:
package Variante1;

import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;


/**
 * Diese Klasse repräsentiert eine modifizierte JComboBox, welche bei der Eingabe von Zeichen die Auswahlliste
 * dynamisch verkleinert oder vergrössert.
 *
 * @author Fabio Ferrari (FFE)
 * @version 1.0
 */
public class Variante1MDynamicComboBox extends JComboBox {
    /**
     * Dieser Vector beinhaltet alle Elemente die in die JComboBox geladen werden sollen. Der Effektive
     * Datenbestand ist im DataModel vorhanden.
     */
    private Vector<String> originalValues;

    /**
     * Enthält alle Listener die sich für den SelectedElementPerformed Event registriert haben.
     */
    private Variante1MDynamicComboBoxEventMulticaster multicaster = new Variante1MDynamicComboBoxEventMulticaster();

    /**
     * Fügt ein MDynamicComboBoxListener der Interessentenliste hinzu.
     *
     * @param l Referenz auf den Listener
     */
    public void addMComboBoxListener(Variante1MDynamicComboBoxListener l) {
        multicaster.add(l);
    }

    /**
     * Entfernt einen MDynamicComboBoxListener aus der Interessentenliste.
     *
     * @param l Referenz auf den zu löschenden Listener
     */
    public void removeMComboBoxListener(Variante1MDynamicComboBoxListener l) {
        multicaster.remove(l);
    }

    /**
     * Gibt die Anzahl der registrierten Listener zurück.
     *
     * @return Anzahl der registrierten Listener
     */
    public int getMDynamicComboBoxListenerCount(){
        return multicaster.getSize();    
    }

    /**
     * Default-Konstruktor der Klasse.
     *
     * @param model Als Parameter wird das Dynamische ComboBoxModel benötigt
     */
    public Variante1MDynamicComboBox(Variante1MDynamicComboBoxModel model) {
        super(model);

        originalValues = (Vector<String>) model.getElements().clone();

        JTextField tf = (JTextField) getEditor().getEditorComponent();
        tf.setDocument(new Variante1MDynamicComboBoxDocument());
        getEditor().addActionListener(new Variante1MDynamicSelectTextActionListener());
    }

    /**
     * Diese Klasse enthält alle Listener die informiert werden wollen, wann ein Element in der ComboBox ausgewählt wurde.
     *
     * @author Fabio Ferrari (FFE)
     * @version 1.0
     */
    private class Variante1MDynamicComboBoxEventMulticaster implements Variante1MDynamicComboBoxListener {
        /**
         * Enthält alle Listener die sich für den SelectedElementPerformed Event registriert haben.
         */
        private Vector listener = new Vector();

        /**
         * Informiert alle registrierten Listener darüber, dass ein Element ausgewählt wurde.
         *
         * @param evt Referenz auf den entsprechenden Event
         */
        public void selectedElementPerformed(Variante1MDynamicComboBoxEvent evt) {
            for (int i = 0; i < listener.size(); i++) {
                ((Variante1MDynamicComboBoxListener) listener.elementAt(i)).selectedElementPerformed(evt);
            }
        }

        /**
         * Entfernt einen Listener aus der Liste.
         * 
         * @param l Referenz auf den zu löschenden Listener
         */
        public void remove(Variante1MDynamicComboBoxListener l) {
            listener.remove(l);
        }

        /**
         * Fügt einen neuen Listener der Interessentenliste hinzu, sofern er nicht schon hinzugefügt wurde.
         * 
         * @param a Referenz auf den interessierten Listener
         */
        public void add(Variante1MDynamicComboBoxListener a) {
            if (!listener.contains(a))
                listener.addElement(a);
        }

        /**
         * Gibt die Anzahl der Listener zurück.
         *
         * @return Anzahl der Listener
         */
        public int getSize() {
            return listener.size();
        }
    }

    /**
     * Das MDynamicComboBoxDoccument beinhaltet die Logik der JComboBox. Normalerweise werden über das PlainDocument
     * einzelne Zeichen bei der Eingabe manipuliert. In unserem Fall wird ein eingegebens Zeichen dem Dokument hinzugefügt
     * und anschliessend die neue Liste mit den neuen Werten geladen.
     *
     * @author Fabio Ferrari (FFE)
     * @version 1.0
     */
    private class Variante1MDynamicComboBoxDocument extends PlainDocument {
        /**
         * Sobald ein Zeichen dem JTextField hinzugefügt werden soll, wird diese Methode aufgerufen.
         * 
         * @param offset Position
         * @param str eingegebenes Zeichen
         * @param a eine Auswahl von eindeutigen Attributen
         * @throws BadLocationException Falls der Wert an einem falschen Ort eingegeben wurde
         */
        public void insertString(int offset, String str, AttributeSet a) throws BadLocationException {
            super.insertString(offset, str, a);

            System.out.println(offset + ": " + str + ": " + ": " + a);
            //Sobald ein Zeichen eingefügt wurde, wird das PopupMenu geöffnet.
            showPopup();
            refresh();
            /**
             * Falls ein String > 1 eingefügt wurde (z.b. bei einem Mausklick im PopupMenu) und dieser String eine
             * gültige Methode darstellt, dann sollen alle Listener darüber informiert werden.
             */
            if (originalValues.contains(str)) {
                multicaster.selectedElementPerformed(new Variante1MDynamicComboBoxEvent(this, str));
//                hidePopup();
            }
        }

        /**
         * Entfernt ein Zeichen aus dem Eingabefeld
         * 
         * @param offset Position im Eingabefeld
         * @param len Anzahl der Zeichen
         * @throws BadLocationException Falls ein Fehler beim Löschen auftaucht.
         */
        public void remove(int offset, int len) throws BadLocationException {
            super.remove(offset, len);
            System.out.println(offset + ": " + len);
            refresh();
        }

        /**
         * Diese Methode aktualisiert die Einträge im Popup werden. Es werden entweder Elemente entfernt oder neu
         * hinzugefügt.
         */
        public void refresh() {
            JTextField tf = (JTextField) getEditor().getEditorComponent();
            String text = tf.getText();
            Variante1MDynamicComboBoxModel originalModel = (Variante1MDynamicComboBoxModel) getModel();

            originalModel.removeAllElements();
            for (int i = 0; i < originalValues.size(); i++) {
                if (originalValues.get(i).startsWith(text) || text.isEmpty()) {
                    originalModel.addElement(originalValues.get(i));
                }
            }

            /**
             * Damit die Elemente aktualisiert werden, muss dieses Menu neu geladen werden.
             */
            if (isPopupVisible()) {
                hidePopup();
                showPopup();
            }
        }
    }

    /**
     * Dieser Listenerklasse setzt den ausgewählten Wert im JTextField.
     *
     * @author Fabio Ferrari (FFE)
     * @version 1.0
     */
    private class Variante1MDynamicSelectTextActionListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            JTextField tf = (JTextField) getEditor().getEditorComponent();
            Object selectedText = getSelectedItem();
            if (selectedText != null) {
                tf.setText(selectedText.toString());
            }
        }
    }


}
####################################################

Code:
package Variante1;

import java.util.EventObject;

/**
 * Diese Klasse repräsentiert einen Event der verteilt wird, wenn ein Element bei der ComboBox ausgewählt wurde.
 *
 * @author Fabio Ferrari (FFE)
 * @version 1.0
 */
public class Variante1MDynamicComboBoxEvent extends EventObject {

    /**
     * Name des ausgewählten Elements
     */
    private String selectedElement;

    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */
    public Variante1MDynamicComboBoxEvent(Object source, String selectedElement) {
        super(source);
        this.selectedElement = selectedElement;
    }

    /**
     * Gibt das markierte Element zurück.
     * 
     * @return selectedElement Markierte Element
     */
    public String getSelectedElement(){
        return selectedElement;
    }
}
####################################################
Code:
package Variante1;

import javax.swing.*;
import java.io.Serializable;
import java.util.Vector;

public class Variante1MDynamicComboBoxModel extends AbstractListModel implements MutableComboBoxModel, Serializable {
    private Vector<String> objects;
    private Object selectedObject = null;

    public Variante1MDynamicComboBoxModel(Vector<String> v) {
        objects = v;
    }

    public void setSelectedItem(Object anObject) {
        if ((selectedObject != null && !selectedObject.equals(anObject)) || selectedObject == null && anObject != null) {
            selectedObject = anObject;
        }
    }

    public Object getSelectedItem() {
        return selectedObject;
    }

    public int getSize() {
        return objects.size();
    }

    public Object getElementAt(int index) {
        if (index >= 0 && index < objects.size()) {
            return objects.elementAt(index);
        } else {
            return null;
        }
    }

    public int getIndexOf(Object anObject) {
        return objects.indexOf(anObject);
    }

    public Vector<String> getElements() {
        return objects;
    }

    public void addElement(Object obj) {
        objects.addElement(obj.toString());
    }

    public void insertElementAt(Object obj, int index) {
    }

    public void removeElementAt(int index) {
        if ((index <= getSize()) && (getSize() != 0) && (index >= 0)) {
            if (getElementAt(index) == selectedObject) {
                if (index == 0) {
                    setSelectedItem(getSize() == 1 ? null : getElementAt(index + 1));
                } else {
                    setSelectedItem(getElementAt(index - 1));
                }
            }
            objects.removeElementAt(index);
        }
    }

    public void removeElement(Object anObject) {
        int index = objects.indexOf(anObject);
        if (index != -1) {
            removeElementAt(index);
        }
    }

    public void removeAllElements() {
        if (objects.size() > 0) {
            objects.removeAllElements();
        }
        selectedObject = null;
    }

    public boolean contains(String str){
        return objects.contains(str);
    }
}
####################################################
Code:
package Variante1;

import javax.swing.*;
import java.awt.event.*;
import java.util.Vector;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;


public class Variante1AppStarter {
    private JFrame refFrame;

    public Variante1AppStarter() {
        initGUIComponents();

        refFrame.pack();
        setPreferences();
        refFrame.setVisible(true);
    }

    private void initGUIComponents() {
        refFrame = new JFrame();
        refFrame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                exitApplication();
            }
        });

        //Was machen mit dieser Logik
        final Variante1MDynamicComboBoxModel originalModel = new Variante1MDynamicComboBoxModel(generateValues());
        final Variante1MDynamicComboBox jrb = new Variante1MDynamicComboBox(originalModel);
//
        //Listener auf dem JRB
        jrb.getEditor().getEditorComponent().addKeyListener(new KeyAdapter(){
            public void keyPressed(KeyEvent e) {
                String text = ((JTextField)jrb.getEditor().getEditorComponent()).getText();
                if((e.getKeyCode() == KeyEvent.VK_ENTER) && originalModel.contains(text) && (text != null) ){
                    System.out.println("Einzelbestimmung ausgewählt: " + ((JTextField)jrb.getEditor().getEditorComponent()).getText());
                }else if(e.getKeyCode() == KeyEvent.VK_DOWN){
                    System.out.println(jrb.getSelectedIndex());
                }
            }
        });
        jrb.addMComboBoxListener(new Variante1MDynamicComboBoxListener(){
            public void selectedElementPerformed(Variante1MDynamicComboBoxEvent evt) {
                System.out.println("Einzelbestimmung ausgewählt: " + ((JTextField)jrb.getEditor().getEditorComponent()).getText());
            }
        });
        
        jrb.addItemListener(new ItemListener(){
            public void itemStateChanged(ItemEvent e) {
                System.out.println("JRB-ItemListener: " + e);
            }
        });
        
        jrb.setEditable(true);
        refFrame.add(jrb);
    }

    private Vector<String> generateValues() {
        Vector<String> values = new Vector<String>();
        String abcd = "abcd";
        String bacd = "bdac";
        String cdab = "cdab";
        String dcab = "dcab";
        for(int i = 0; i < 10; i++){
            values.add(abcd);
            values.add(bacd);
            values.add(cdab);
            values.add(dcab);
            abcd = "a" + abcd;
            bacd = "b" + bacd;
            cdab = "c" + cdab;
            dcab = "d" + dcab;
        }
        return values;
    }

    /**
     * Speichern der Einstellungen
     * Fensterposition, -grösse
     *
     * @throws java.util.prefs.BackingStoreException
     */
    public void storePreferences() throws BackingStoreException {
        // Ermitteln des zuständigen Knotens
        Preferences prefs = Preferences.userNodeForPackage(getClass());
        // Speichern der Fenster-Position
        prefs.putInt("xpos", refFrame.getLocation().x);
        prefs.putInt("ypos", refFrame.getLocation().y);
        // Speichern der Fenster-Größe
        prefs.putInt("width", refFrame.getSize().width);
        prefs.putInt("height", refFrame.getSize().height);

        // Zur Sicherheit alles zurückschreiben
        prefs.flush();
    }

    /**
     * Setzen der Fenstergrösse und Position des letzten Zustandes
     */
    public void setPreferences() {
        // Ermitteln des zuständigen Knotens
        Preferences prefs = Preferences.userNodeForPackage(getClass());
        // Setzen der Position (Default: 0,0)
        refFrame.setLocation(prefs.getInt("xpos", 0), prefs.getInt("ypos", 0));
    }

    /**
     * Beendet den Editor.
     */
    public void exitApplication() {
        try {
            storePreferences();
        } catch (BackingStoreException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        refFrame.dispose();
    }
    
    public static void main(String[] args) {
        new Variante1AppStarter();
    }

}
 
Ich habe nun einen anderen Ansatz gewählt und zwar indem ich der JComboBox zusätzlich zur bestehenden ComboBox ein PopupMenü hinzugefügt habe.

Die bestehende Jcombobox zu manipulieren, wie dies im oberen Code der Fall ist geht zwar auch, aber eben es gibt einige Fälle wo es unschöne Nebeneffekte gibt.
 
Status
Nicht offen für weitere Antworten.

Zurück
Oben