2012-05-03 19 views
9

Próbuję zbudować javax.swing.JTextField z javax.swing.JList dla automatycznego uzupełniania, np. Google.Automatyczne uzupełnianie JTextField i klawiszy strzałek

  • Kiedy Wpisz słowo, Google pokazać kilka mecze i

    • gdy naciśnij    ▼     mogę wybrać jakiś mecz użyciu     ▲     i     ▼     i
    • może edytować swoje wejście z       ◀ i ▶        .
    • Po naciśnięciu Enter                       kluczową przeszukiwanie zawartości pudełka.
    • Po naciśnięciu przycisku skrzynia zmieni się na pierwotne wejście.

Moja aplikacja jest o Bible i chcę szuka konkretnego słowa, kiedy uczę Słowo. Widziałem Java2sAutoTextField, ale nie mam tego szczególnego zachowania za pomocą klawiszy strzałek.

+1

Czy można rozszerzyć JTextField i nadać mu JList jako członek, a następnie zarządzać naciśnięciem przycisku ręcznie z JTextField? Również, czy lista rozwijana nie zostanie odcięta na krawędzi kontenera? Elementy obrotowe są lekkie i wszystkie. – Tharwen

+0

@mKorbel Jeśli istnieje lepsze rozwiązanie, chciałbym wiedzieć więcej. Właściwie szukam zachowania podobnego do Google, niekoniecznie z 'JList'. –

+0

@Paul Vargas cóż, popatrzę na to, myślę, że to pytanie może być bardzo proste do kodowania – mKorbel

Odpowiedz

9

Wymaga to niestandardowego zakodowanego komponentu. Zdecydowanie klasa, która rozszerza JTextField i w tej klasie masz JPopupMenu, które będzie zawierać twoją listę JList. Będziesz musiał ustawić JPopupMenu tuż pod polem tekstowym, aby wyglądał jak 1 komponent.

Następną sztuczką jest filtrowanie podczas pisania. Zwykle robię to za pomocą Java6 TableRowSorter w połączeniu z JTable, do którego wstępnie wypełniam dane. Będziesz potrzebował zmienić słuchaczy na JTextField i przechwycić każdy klawisz wpisany i pobrać swoje dane.

  1. naciśnięty klawisz
  2. Wykonaj zapytanie w dB (lub jakiś przechowywanie danych, aby uzyskać podobne wpisy)
  3. wypełnić JTable z tych aplecie
  4. Set RowFilter z regex na podstawie wpisu JTextField do przefiltrować przez pobranych danych
  5. Zarządzanie swoje działania z kluczowymi słuchaczy

EDIT

Przygotowałem przykładową aplikację do swingowania, aby pokazać, co napisałem. To jest przykład kopiowania/wklejania i powinien działać od razu (wymaga JDK 1.6+). Zasadniczo otrzymałem to, co chciałem, i dodałem komentarze w miejscach, w których mówię, aby wypełnić puste miejsca. Na przykład wydarzenie Escape zostanie zużyte i możesz zrobić, co tylko zechcesz.

Metoda initTableModel() właśnie inicjuje model tabeli z danymi. Zwykle chcesz dynamicznie wypełniać model tabeli danymi z bazy danych lub czymś podobnym. Wiele można by poprawić, ale jest to na przykład sake;) Więc to powinien być dobry przykład, abyś mógł zmodyfikować swój cel. Więcej niż w tym i musisz mi zapłacić $$$ :)

package test.text.googleclone; 

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Rectangle; 
import java.awt.Toolkit; 
import java.awt.Window; 
import java.awt.event.ActionEvent; 
import java.awt.event.KeyEvent; 
import java.awt.event.KeyListener; 
import java.util.regex.PatternSyntaxException; 
import javax.swing.AbstractAction; 
import javax.swing.BorderFactory; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JPopupMenu; 
import javax.swing.JTable; 
import javax.swing.JTextField; 
import javax.swing.KeyStroke; 
import javax.swing.ListSelectionModel; 
import javax.swing.RowFilter; 
import javax.swing.event.DocumentEvent; 
import javax.swing.event.DocumentListener; 
import javax.swing.table.DefaultTableModel; 
import javax.swing.table.TableRowSorter; 

public class SearchAutoFillTest { 

private JFrame frame = null; 
private JTextField searchField = null; 
private JPopupMenu popup = null; 

private JTable searchTable = null; 
private TableRowSorter<DefaultTableModel> rowSorter = null; 
private DefaultTableModel searchTableModel = null; 

public SearchAutoFillTest() { 
    searchTableModel = new DefaultTableModel(); 
    initTableModel(); 

    rowSorter = new TableRowSorter<DefaultTableModel>(searchTableModel); 
    searchTable = new JTable(searchTableModel); 
    searchTable.setRowSorter(rowSorter); 
    searchTable.setFillsViewportHeight(true); 
    searchTable.getColumnModel().setColumnSelectionAllowed(false); 
    searchTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); 
    searchTable.getTableHeader().setReorderingAllowed(false); 
    searchTable.setPreferredSize(new Dimension(775, 100)); 
    searchTable.setGridColor(Color.WHITE); 

    searchField = new JTextField(); 
    searchField.getDocument().addDocumentListener(new DocumentListener() { 
     @Override 
     public void changedUpdate(DocumentEvent e) { 
      showPopup(e); 
     } 

     @Override 
     public void insertUpdate(DocumentEvent e) { 
      showPopup(e); 
     } 

     @Override 
     public void removeUpdate(DocumentEvent e) { 
      showPopup(e); 
     } 
    }); 

    searchField.addKeyListener(new KeyListener() { 
     @Override 
     public void keyTyped(KeyEvent e) { 

     } 

     @Override 
     public void keyReleased(KeyEvent e) { 
      int code = e.getKeyCode(); 
      switch(code) 
      { 
       case KeyEvent.VK_UP: 
       { 
        cycleTableSelectionUp(); 
        break; 
       } 

       case KeyEvent.VK_DOWN: 
       { 
        cycleTableSelectionDown(); 
        break; 
       } 

       case KeyEvent.VK_LEFT: 
       { 
        //Do whatever you want here 
        break; 
       } 

       case KeyEvent.VK_RIGHT: 
       { 
        //Do whatever you want here 
        break; 
       } 
      } 
     } 

     @Override 
     public void keyPressed(KeyEvent e) { 

     } 
    }); 

    KeyStroke keyStroke = KeyStroke.getKeyStroke("ESCAPE"); 
    searchField.getInputMap().put(keyStroke, "ESCAPE"); 
    searchField.getActionMap().put("ESCAPE", new AbstractAction() { 
     @Override 
     public void actionPerformed(ActionEvent e) { 
      //Do what you wish here with the escape key. 
     } 
    }); 

    popup = new JPopupMenu(); 
    popup.add(searchTable); 
    popup.setVisible(false); 
    popup.setBorder(BorderFactory.createEmptyBorder()); 

    JPanel searchPanel = new JPanel(new BorderLayout(5, 5)); 
    searchPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); 
    searchPanel.add(searchField, BorderLayout.CENTER); 

    frame = new JFrame(); 
    frame.setLayout(new BorderLayout(5, 5)); 
    frame.add(searchPanel, BorderLayout.NORTH); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    frame.setSize(800, 500); 
    center(frame); 
    frame.setVisible(true); 
} 

private final void newFilter() { 
    RowFilter<DefaultTableModel, Object> rf = null; 

    try { 
     rf = RowFilter.regexFilter(getFilterText(), 0); 
    } 
    catch(PatternSyntaxException e) { 
     return; 
    } 

    rowSorter.setRowFilter(rf); 
} 

private final String getFilterText() { 
    String orig = searchField.getText(); 
    return "("+orig.toLowerCase()+")|("+orig.toUpperCase()+")"; 
} 

private void showPopup(DocumentEvent e) { 
    if(e.getDocument().getLength() > 0) { 
     if(!popup.isVisible()) { 
      Rectangle r = searchField.getBounds(); 
      popup.show(searchField, (r.x-4), (r.y+16)); 
      popup.setVisible(true); 
     } 

     newFilter(); 
     searchField.grabFocus(); 

    } 
    else { 
     popup.setVisible(false); 
    } 
} 

private void cycleTableSelectionUp() { 
    ListSelectionModel selModel = searchTable.getSelectionModel(); 
    int index0 = selModel.getMinSelectionIndex(); 
    if(index0 > 0) { 
     selModel.setSelectionInterval(index0-1, index0-1); 
    } 
} 

private void cycleTableSelectionDown() { 
    ListSelectionModel selModel = searchTable.getSelectionModel(); 
    int index0 = selModel.getMinSelectionIndex(); 
    if(index0 == -1) { 
     selModel.setSelectionInterval(0, 0); 
    } 
    else if(index0 > -1) { 
     selModel.setSelectionInterval(index0+1, index0+1); 
    } 
} 

private void initTableModel() { 
    String[] columns = new String[] {"A"}; 
    String[][] data = new String[][] 
    { 
     new String[] {"a"}, 
     new String[] {"aa"}, 
     new String[] {"aaab"}, 
     new String[] {"aaabb"}, 
     new String[] {"aaabbbz"}, 
     new String[] {"b"}, 
     new String[] {"bb"}, 
     new String[] {"bbb"}, 
     new String[] {"bbbbbbb"}, 
     new String[] {"bbbbbbbeee"}, 
     new String[] {"bbbbbbbeeexxx"}, 
     new String[] {"ccc"}, 
     new String[] {"cccc"}, 
     new String[] {"ccccc"}, 
     new String[] {"cccccaaaa"}, 
     new String[] {"ccccccaaaa"}, 
    }; 

    searchTableModel.setDataVector(data, columns); 
} 

private void center(Window w) { 
    int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width; 
    int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height; 

    int windowWidth = w.getWidth(); 
    int windowHeight = w.getHeight(); 

    if (windowHeight > screenHeight) { 
     return; 
    } 

    if (windowWidth > screenWidth) { 
     return; 
    } 

    int x = (screenWidth - windowWidth)/2; 
    int y = (screenHeight - windowHeight)/2; 

    w.setLocation(x, y); 
} 

public static void main(String ... args) { 
    new SearchAutoFillTest(); 
} 
} 
+0

Uważam, że jest bardzo interesująca. Postaram się przesłać Ci opinię. –

+0

To naprawdę jest altruistyczne oprogramowanie (nie zamierzam ładować moich przyjaciół), więc w pewnym sensie przyczyniłeś się do tego. –

+0

Fajny człowiek, to jest niesamowite .. Mogę dać więcej niż +1. bardzo ładne, dziękuję za udostępnienie .. – Anubis

0

Domyślnym zachowaniem jest to, że wszystkie kluczowe zdarzenia trafiają do komponentu, który ma fokus. Musisz więc zidentyfikować klucze, które naprawdę powinny przejść do drugiego komponentu i zainstalować oba.

W tym słuchniku ​​można przekazywać zdarzenia do drugiego komponentu.

Zobacz this answer jak wysłać zdarzenie do nowego komponentu. W twoim przypadku, source musi być drugim elementem (listą, jeśli twoje pole tekstowe otrzymało pierwotnie zdarzenie i na odwrót).

1

Brzmi jak chcesz JComboBox (patrz Swing guide) zamiast JTextField/JList.

Oczywiście, masz przycisk rozwijany, ale są możliwe sposoby radzenia sobie z tym - patrz here.

1

Byłoby coś wzdłuż tych linii:

import java.awt.event.KeyEvent; 
import java.awt.event.KeyListener; 
import java.util.ArrayList; 

import javax.swing.*; 


public class Component extends JComponent { 
    private final static String[] terms = {"Jesus", 
     "Jesus walks on water" //... 
    }; 
    private static ArrayList<String> recent = new ArrayList<String>(); 

    JTextField jtf; 
    JList jl; 
    public Component(){ 
     // set up design 
     jtf = new JTextField(); 
     jtf.setSize(this.getWidth() - 25, 25); 
     this.add(jtf); 
     //... 
     // add key listeners 
    } 
    class Listener implements KeyListener{ 

     @Override 
     public void keyPressed(KeyEvent arg0) { 

     } 

     @Override 
     public void keyReleased(KeyEvent arg0) { 

     } 

     @Override 
     public void keyTyped(KeyEvent arg0) { 
      if (arg0.getKeyCode() == KeyEvent.VK_DOWN){ 
       // set next item on list 
      } 
      else if (arg0.getKeyCode() == KeyEvent.VK_UP){ 
       // set previous item on list 
      } 
      else if (arg0.getKeyCode() == KeyEvent.VK_ENTER){ 
       // search 
      } 
      else if (arg0.getKeyCode() == KeyEvent.VK_ESCAPE){ 
       jtf.setText(""); 
      } 
            else{ 
             // check list for matches 
            } 
     } 

    } 
} 
3
  • Wpisać JTextField umieszczane JToolBar/MenuBar, zauważy musisz posortować ArrayList Przed użyciem

  • użyj nieskorowanej JDialog zamiast JPopup (nadal masz kilka ważnych błędów),

    a) tylko tworzyć jeden JDialog z nadrzędnego do JTextField lub JMenuBar lub JFrame,

    b) zawsze sprawdzić getBounds z Autouzupełniania JTextField przed widocznym JDialog na ekranie, to Oszacowania służą possitioning JDialog poprawnie na ekranie

    c) opasania JDialog # setVisible (prawda) do invokeLater()

  • przesłanianie Escape for JDialog.setVisible(false)

  • tam umieszczone blisko/ukryj JButton do avoiding overrive rest of important methods on focusLost (ten kalendarz mam excelent obejście na focusLost, kliknięcie myszką, etc ...., czy może być bardzo łatwo zastąpić funkcjonalność kalendarza z wynikiem z Komparatora, musisz pobrać kod źródłowy)

  • możesz umieścić tam (mój widok) przyciski 6/9/max 12, możesz usunąć JButton Feels by setBackground (Color.white) na przykład, nie możesz, proszę, nie rób tego z JDialog i tymi JButtonami, praca będzie tylko dla setText ("wynik z Komparatora")

  • w przypadku, gdy twoja ArrayList dla autouzupełniania JTextField został posortowany, następnie masz dwa wybory: a) najłatwiejsze uprzedzenie z funkcji autouzupełniania przez dodanie fils osobnej tablicy dla setText() dla 6/9/max 12 butto ns na wyskakującym JDialogu, jeśli ustawiszBackground (Color.white), to nie obchodzi cię jakoś ukryć JButtons bez tekstu

    b) innym sposobem może być stworzenie własnego Komparatora do wyszukiwania (ta sama funkcja Autouzupełniania) najpierw 6/9/max 12 meczów,

  • dla przechwytywanie zdarzeń z 6/9/max 12 JButtons używać putClientProperty lub EventHandler lub Swing Actions, gdzie tylko można przetestować jeśli tekst isEmpty :-),

  • może Swing Actions może być najlepszym sposobem, ponieważ jego zdarzenia można scalla i można enabled/disable (jeśli JButtons t ext isEmpty) wyjście z tej akcji domyślnie

Powiązane problemy