Autovervollständigung in einer Tabelle

Jhue89

Bekanntes Mitglied
Guten Morgen Zusammen,

Ich suche nach einer möglichkeit vorgefertige Wörte als Vorschlag in ein Tabellen Textfeld anzuzeigen.
Also eine Art Autovervollständigung um Schriebfehler zu vermeiden.
Die Autovervollständigung sollte dann bei jedem neuen Wort angezeigt werden.

Bislange habe ich nur was mit Jface gefunden.
 
Es sollten mehrere Wörter zur verfügung stehen. Ich hatte zuerst dran gedacht in das Tabellenfeld eine Combobox mit den entsprechenden eintragen einzufügen. Aber in dieses Feld müssen mehrer Wörter eingefügt werden mit einer Combobox habe ich die flexibilität nicht.

zb: 4* Comboxeintrag 1 + Comboboxeintrag 5.
 
Du willst in einer Zelle mehrere Wörter eingeben können und für jedes Wort eine Autovervollständigung zur Verfügung haben? Nachtrag: Und dabei sollen jeweils mehrere Wörter zur Auswahl stehen?
 
Guten Morgen Zusammen,

Ich suche nach einer möglichkeit vorgefertige Wörte als Vorschlag in ein Tabellen Textfeld anzuzeigen.
Also eine Art Autovervollständigung um Schriebfehler zu vermeiden.
Die Autovervollständigung sollte dann bei jedem neuen Wort angezeigt werden.

Bislange habe ich nur was mit Jface gefunden.
Welche UI Api benutzt du denn?
 
Ja eine fertige lösung wäre schon super. Leider bin ich an Swing gebunden. Oder kann das gemixt werden?
Sorry ich weiß nicht genau ob Swing da flexible ist. SWT und JavaFX kann auf jeden Fall Interoperabilität mit Swing, ob das umgekehrt auch funktioniert ist fraglich.

Dem Beispiel nach https://docs.oracle.com/javase/8/javafx/interoperability-tutorial/swing-fx-interoperability.htm scheint es zu gehen.

Persönlich finde ich es nicht so schön, wegen Event Handling. Und wegen eines kleinen Erweiterung würde ich keine neue UI Lib einbinden.
 
Das ist eine fertige Lösung, die entsprechend konfigurierbar ist. Dürfte in Deinem Fall aber kein größeres Problem darstellen.

Das Callback dient dazu, dass Du die Freiheit hast, das Ergebnis der Liste zu bestimmen. Du willst ja mit Strings arbeiten, sagen wir mal, angezeigt werden sollen die Wörter, die mit der Eingabe beginnen, wobei wenigstens zwei Zeichen eingegeben werden müssen:
Java:
public class StringAutoCompleteCallBack extends DefaultAutoCompleteCallBack<String> {
    private final List<String> choices;

    public StringAutoCompleteCallBack(List<String> choices) {
        this.choices = choices;
    }

    @Override
    public List<String> getProposals(String input) {
        if ( input.length() < 2 ) {
            return Collections.emptyList();
        }

        final String lower = input.toLowerCase();
        final List<String> result = new ArrayList<>();

        for (String c : choices) {
            if (c.startsWith(lower)) {
                result.add(c);
            }
        }

        return result;
    }

    @Override
    public String getStringToInsert(String value) {
        return value;
    }
}

Der Rest ist einfach:
Java:
public JTextField newAutoCompleteField(List<String> choices) {
    JTextField field = new JTextField();
    AutoCompleteBehaviour<String> autoComplete = new AutoCompleteBehaviour<String>();
    autoComplete.setCallBack(new StringAutoCompleteCallBack(choices));
    autoComplete.setListCellRenderer(new DefaultListCellRenderer());
    autoComplete.setInitialPopupSize(new Dimension(100,300));
    autoComplete.setVisibleRowCount(3);
    autoComplete.attachTo(field);
    return field;
}

Achtung: alles ungetestet und nur hier im Editor geschrieben. Kann sein, dass es nicht funktioniert.
 
Dann hast du aber was schönes gezaubert.
Um das zu bei mir einzufügen muss ich eine Klasse "StringAutoCompleteCallBack" erstellen oder?
Woher kommt denn "DefaultAutoCompleteCallBack<String>"

Den unteren Teil füge ich dann in mein eigentliches Programm ein oder?

Sorry der Nachfrage aber mir fehlt noch einiges an Übung.
 
Um das zu bei mir einzufügen muss ich eine Klasse "StringAutoCompleteCallBack" erstellen oder?
Woher kommt denn "DefaultAutoCompleteCallBack<String>"

Den unteren Teil füge ich dann in mein eigentliches Programm ein oder?
Ja, und DefaultAutoCompleteCallBack kommt aus dem https://github.com/toby1984/swing-a...swing/autocomplete/AutoCompleteBehaviour.java

Allerdings, sehe ich gerade, muss es extends AutoCompleteBehaviour.DefaultAutoCompleteCallBack<String> heißen.
 
Also ich hab den Code mal mit reingenommen und der Methode einen Paramter übergeben und für den return ein Textfeld zugewiesen.
Ich denke so hast du dir das gedacht oder?

textField = newAutoCompleteField(list1);

leider bekomme ich folgende Exception mit EDT kann ich aber leider nichts anfangen.

Java:
Exception in thread "main" java.lang.IllegalStateException: This method must be called from the EDT
    at classes.AutoCompleteBehaviour.attachTo(AutoCompleteBehaviour.java:573)
 
das Verstehe ich jetzt grade nicht.

Normalerweise erstelle ich die Objekte doch hier
Java:
public class MainWindow extends JFrame {
         
public static void main(String[] args){}

    public MainWindow()
    {
         JTextField textFieldTEST = new JTextField();
 
Das Programm startet in main, da befindest Du Dich im main-Thread. Mit Hilfe von invokeLater sorgt man dafür, dass die UI-Geschichten auch wirklich im EDT ablaufen. Meine Vorlage, die ich fürs Forum verwende, sieht so aus:
Java:
import javax.swing.*;

public class Test {

    public void run() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setSize(800, 600);
        frame.setVisible(true);
    }

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

In main wird mit invokeLater nun dafür gesorgt, dass new Test() und das anschließende run() im Event Dispatch Thread (EDT) abläuft.
 
Ok, das habe ich soweit verstanden. Dafür musste ich erst meinen Frame anders erstellen.

Leider funktioniert das aber noch nicht.
Muss ich dafür nicht Actionlistner einfügen und die Methode aufrufen sobald eine eingabe erfolgt?
 
Java:
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.*;
import java.util.*;

class StringAutoCompleteCallback extends AutoCompleteBehaviour.DefaultAutoCompleteCallback<String> {
    private final List<String> choices;

    public StringAutoCompleteCallback(List<String> choices) {
        this.choices = choices;
    }

    @Override
    public List<String> getProposals(String input) {
        if ( input.length() < 2 ) {
            return Collections.emptyList();
        }

        final String lower = input.toLowerCase();
        final List<String> result = new ArrayList<>();

        for (String c : choices) {
            if (c.toLowerCase().startsWith(lower)) {
                result.add(c);
            }
        }

        return result;
    }

    @Override
    public String getStringToInsert(String value) {
        return value;
    }
}

public class Test {

    private JTextField newAutoCompleteField(List<String> choices) {
        JTextField field = new JTextField();
        AutoCompleteBehaviour<String> autoComplete = new AutoCompleteBehaviour<String>();
        autoComplete.setCallback(new StringAutoCompleteCallback(choices));
        autoComplete.setListCellRenderer(new DefaultListCellRenderer());
        autoComplete.setInitialPopupSize(new Dimension(100,300));
        autoComplete.setVisibleRowCount(3);
        autoComplete.attachTo(field);
        return field;
    }

    public void run() {
        List<String> list = List.of("Osterhase", "Osterei", "Eierfarbe");
        JTextField tf = newAutoCompleteField(list);
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(tf, BorderLayout.NORTH);
        frame.setSize(800, 600);
        frame.setVisible(true);
    }

    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(() -> new Test().run());
    }
}

Tippe Oster, dann Strg+Leertaste.
 
Jetzt funktioniert es bei mir auch. Super danke. Ich hab den Fehler auch gefunden. Das Textfeld darf erst nach
Java:
 JTextField tf = newAutoCompleteField(list);
zum Panel hinzugefügt werden ansonsten werden die Einträge nicht angezeigt.

Kann ich das denn jetzt auch einer Tabellenspalte zuweisen?
 
Sicher, hier mal für die erste Spalte:
Java:
    public void run() {
        List<String> list = List.of("Osterhase", "Osterei", "Eierfarbe");
        JTextField tf = newAutoCompleteField(list);

        JTable table = new JTable(new DefaultTableModel(5,3));
        // Optional: table.setSurrendersFocusOnKeystroke(true);
        table.getColumnModel().getColumn(0).setCellEditor(new DefaultCellEditor(tf));

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(new JScrollPane(table));
        frame.setSize(800, 600);
        frame.setVisible(true);
    }
Ohne die optionale Zeile musst Du per Klick oder F2 die Bearbeitung beginnen. Mit der Zeile funktioniert es auch, wenn Du drauf los tippst.
 
Hallo Zusammen,

ich muss das Thema leider noch mal los machen.

Momentan reagiert die o.g Funktionen ja auf ein Leerzeichen. Ist es möglich auf ein Leerzeichen und einem "." zu reagieren und dabei dabei direkt das Popup Fenster zu öffnen?

Danke und Gruß
 
Ok, eigentlich stelle ich mir die Autovervollständigung ähnlich vor wie in Eclipse. Wenn ich Z.b "Ost" + STRG +Leerzeichen eingebe werden die Auswahlmöglichkeiten eingezeigt.
Ich wähle Ostern aus und gebe dahinter "." ein und mir werden die anderen Einträge angezeigt.
Also neben STRG+Leerzeichen auch auf "." zu reagieren

Damit möchte ich Eingabefehler verhinden.

Als Konkretes Beispiel wäre z.b

Eingabe:Auswahlliste:
SchSchachtel1
Schachtel1.X

Ich hoffe das ich es etwas deulicher machen konnte was ich möchte. Ist wie immer etwas schwer zu erklären
 
Ich hab selber mal ein bisschen nachgeforscht.

Java:
   public void keyPressed(java.awt.event.KeyEvent e)
        {
            // CTRL+SPACE is hotkey
            if ( e.getKeyCode() == KeyEvent.VK_SPACE && isCtrlDown(e) ||e.getKeyCode() == KeyEvent.VK_PERIOD)
            {
                try
                {
                    final InitialUserInput p = callback.getInitialUserInput( editor , editor.getCaretPosition() );
                    final Rectangle rect = editor.modelToView( p.caretPosition );           
                    showPopup( rect , e.getWhen() , p.userInput , p.caretPosition );
                }
                catch (BadLocationException e1)
                {
                    e1.printStackTrace();
                }
            }
            
            if (e.getKeyCode() == KeyEvent.VK_PERIOD) // Das habe ich hinzugefügt, Popup Menü wird angezeigt aber ohne Eintragungen
            {
                 try
                 {
                     final InitialUserInput p = callback.getInitialUserInput( editor, editor.getCaretPosition() );
                     final Rectangle rect = editor.modelToView( p.caretPosition );           
                     showPopup( rect , e.getWhen() , null , p.caretPosition );
                 }
                 catch (BadLocationException e1)
                 {
                     e1.printStackTrace();
                 }
            }

Also wenn "." Drücke wird mir zumindest schonmal das Popup menü angezeigt.
Aber ich muss ihm jetzt irgendwie mitteilen, dass ich alle Einträge sehen möchte.
Das habe ich noch nicht gefunden.
 
Einfach mal
Java:
        if ( input.length() < 2 ) {
            return Collections.emptyList();
        }
entfernen 🙂
So einfach ist es dann doch nicht 😉.

Nein im ernst die Liste bleibt leer.
Vermutlich weil er nach dem "." sucht den gibt es natürlich nicht in der Liste der müsste eigentlich wie ein Leerzeichen gesehen werden.
 
Ja, das ist richtig. Dann schau halt nach, ob input mit einem Punkt beginnt, wenn ja, schneidest Du ihn ab (input.substring(1)).
 
Guten Morgen Zusammen,
ich hab jetzt schon wieder einige Stunden rumprobiert. Leider ohne Erfolg.
Die Liste bleibt immernoch leer.

Der Punkt soll ja auch stehen bleiben und nicht abgeschnitten werden. Ab dem "." müsste eigentlich ein neuer Startpunkt gesetzt werden.
Aber ich find das nicht.

Oder bin ich jetzt total auf dem Holzweg?
 
In AutoCompleteBehaviour den keyListener ändern und die zusätzliche Methode einfügen (der Code wird nur ausgelagert, damit man ihn in den Zeilen 7 und 9 nicht wiederholen muss). Das invokeLater sorgt dafür, dass die ausstehenden Ereignisse erst noch abgearbeitet werden, bevor das Popup angezeigt wird.

Java:
    private final KeyAdapter keyListener = new KeyAdapter() 
    {
        public void keyPressed(java.awt.event.KeyEvent e) 
        {
            // CTRL+SPACE is hotkey
            if ( e.getKeyCode() == KeyEvent.VK_SPACE && isCtrlDown(e)) {
                showPopup(e);
            } else if (e.getKeyChar() == '.') {
                SwingUtilities.invokeLater(() -> showPopup(e));
            }
        }
    };


    private void showPopup(java.awt.event.KeyEvent e) {
        try 
        {
            final InitialUserInput p = callback.getInitialUserInput( editor , editor.getCaretPosition() );
            final Rectangle rect = editor.modelToView( p.caretPosition );           
            showPopup( rect , e.getWhen() , p.userInput , p.caretPosition );
        } 
        catch (BadLocationException e1) 
        {
            e1.printStackTrace();
        }
    }

Und in Test.java, class StringAutoCompleteCallback ersetzen durch
Java:
class StringAutoCompleteCallback extends AutoCompleteBehaviour.DefaultAutoCompleteCallback<String> {
    private final List<String> choices;

    public StringAutoCompleteCallback(List<String> choices) {
        this.choices = choices;
    }

    @Override
    public List<String> getProposals(String input) {
        if (input.isEmpty()) {
            return new ArrayList<>(choices);
        }

        final String lower = input.toLowerCase();
        final List<String> result = new ArrayList<>();

        for (String c : choices) {
            if (c.toLowerCase().startsWith(lower)) {
                result.add(c);
            }
        }

        return result;
    }

    @Override
    protected boolean isSeparatorChar(char c ) 
    {
        return c == '.' || Character.isWhitespace( c );
    }

    @Override
    public String getStringToInsert(String value) {
        return value;
    }
}

Dürfte dem ziemlich nahe kommen, was du willst.
 

Neue Themen


Zurück
Oben