2012-05-03 44 views
9

Tôi đang cố gắng tạo một javax.swing.JTextField với javax.swing.JList để tự động hoàn thành như Google.Tự động hoàn thành JTextField và các phím mũi tên

  • Khi ghi một từ, Google hiển thị nhiều trận

    • khi một báo chí các     ▼     tôi có thể chọn một số trận đấu sử dụng     ▲        ▼    
    • có thể chỉnh sửa đầu vào của tôi với     ◀      ▶    .
    • Khi tôi bấm     Nhập                   khóa tìm kiếm nội dung trong hộp.
    • Khi bấm Esc thay đổi hộp thành đầu vào gốc.

aplication của tôi là về Bible và tôi muốn tìm kiếm một từ cụ thể khi tôi đang nghiên cứu Word. Tôi đã thấy Java2sAutoTextField nhưng không có hành vi cụ thể này với các phím mũi tên.

+1

Bạn có thể mở rộng JTextField và đặt cho nó một JList làm thành viên, sau đó quản lý các lần nhấn phím theo cách thủ công từ JTextField không? Ngoài ra, danh sách thả xuống sẽ không bị cắt ở rìa của vùng chứa không? Các thành phần Swing có trọng lượng nhẹ 'n'. – Tharwen

+0

@mKorbel Nếu có giải pháp tốt hơn, tôi muốn biết thêm. Trên thực tế, tôi đang tìm kiếm một hành vi tương tự như Google, không nhất thiết với 'JList'. –

+0

@Paul Vargas Tôi sẽ xem xét điều đó, tôi nghĩ rằng câu hỏi này có thể rất đơn giản để mã hóa – mKorbel

Trả lời

9

Điều này cần một thành phần được mã hóa tùy chỉnh. Chắc chắn là một lớp mở rộng JTextField và trong lớp đó bạn có một JPopupMenu sẽ chứa JList của bạn. Bạn sẽ phải đặt JPopupMenu ngay dưới trường văn bản để nó trông giống như 1 thành phần.

Bí quyết tiếp theo của bạn là lọc khi bạn nhập. Tôi thường làm điều này bằng cách sử dụng Java6 TableRowSorter cùng với một JTable mà tôi điền sẵn nó với dữ liệu. Bạn sẽ cần một số người nghe thay đổi trên JTextField và chặn mỗi khóa được nhập và tìm nạp dữ liệu của bạn.

  1. chính ép
  2. Thực hiện truy vấn trong DB (hoặc một số lưu trữ dữ liệu để có được mục tương tự)
  3. cư JTable với những entires
  4. Set RowFilter với regex dựa trên JTextField nhập để lọc thông qua dữ liệu lấy
  5. Quản lý hành động của bạn với người nghe chính

EDIT

Tôi đã bỏ qua ứng dụng xoay mẫu để hiển thị những gì tôi đã nêu. Đây là một ví dụ sao chép/dán và nên làm việc ngay lập tức từ bat (cần JDK 1.6+). Về cơ bản tôi có được những gì bạn muốn và tôi đặt bình luận ở những nơi mà tôi nói với bạn để điền vào chỗ trống .. ví dụ như sự kiện phím Escape được tiêu thụ và bạn có thể làm bất cứ điều gì bạn muốn với nó.

Phương thức initTableModel() chỉ khởi tạo mô hình bảng với dữ liệu. Thông thường bạn sẽ muốn tự động điền mô hình bảng với dữ liệu từ cơ sở dữ liệu hoặc một cái gì đó. Nhiều thứ có thể được điều chỉnh, nhưng đây là ví dụ vì lợi ích;) Vì vậy, đây là một ví dụ đủ tốt để bạn có thể sửa đổi để hoàn thành mục tiêu của mình. Bất kỳ hơn này và bạn phải trả tiền cho tôi $$$ :)

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

Tôi thấy nó rất thú vị. Tôi sẽ cố gắng gửi phản hồi cho bạn. –

+0

Nó thực sự là một phần mềm vị tha (tôi không có ý định tính phí bạn bè của tôi), vì vậy trong một cách, bạn đã góp phần vào nguyên nhân. –

+0

Người đàn ông tuyệt vời này thật tuyệt .. tôi có thể cung cấp thêm +1 đó. Rất đẹp, cảm ơn cho chia sẻ.. – Anubis

0

Hành vi mặc định là tất cả các sự kiện quan trọng đều đi đến thành phần có tiêu điểm. Vì vậy, những gì bạn cần làm là xác định các phím mà thực sự nên đi đến các thành phần khác và cài đặt một KeyListener cho cả hai.

Trong trình nghe đó, bạn có thể chuyển tiếp sự kiện đến thành phần khác.

Xem this answer cách gửi sự kiện đến thành phần mới. Trong trường hợp của bạn, source phải là thành phần khác (danh sách, nếu trường văn bản của bạn ban đầu nhận được sự kiện và ngược lại).

1

Có vẻ như bạn muốn có JComboBox (xem Swing guide) thay vì JTextField/JList.

Tất nhiên, sau đó bạn có một nút thả xuống, nhưng có nhiều cách để giải quyết vấn đề này - xem here.

1

Nó sẽ là một cái gì đó dọc theo những dòng:

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
  • sử dụng AutoComplete JTextField đặt vào JToolBar/MenuBar, chú ý bạn phải để sắp xếp ArrayList trước khi sử dụng,

  • sử dụng JDialog chưa được định dạng thay vì JPopup (vẫn có một vài lỗi quan trọng),

    a) chỉ tạo một JDialog với bố mẹ cho JTextField hoặc JMenuBar hoặc JFrame,

    b) luôn tìm kiếm getBounds từ AutoComplete JTextField trước khi có thể nhìn thấy JDialog trên màn hình, Bounds này là dành cho possitioning JDialog một cách chính xác trên màn hình

    c) bọc JDialog # setVisible (true) cho invokeLater()

  • override Escape for JDialog.setVisible(false)

  • đặt ở đó gần/hide JButton để avoiding overrive rest of important methods on focusLost (lịch này đã có cách giải quyết xuất sắc trên focusLost, mouseclick, vv ...., có thể thay thế funcionality lịch dễ dàng bằng kết quả từ Comparator, bạn phải tải mã nguồn)

  • bạn có thể đặt ở đó (xem của tôi) 6/9/max 12, bạn có thể xóa JButton Feels bằng setBackground (Color.white) ví dụ, bạn cann't, xin vui lòng không làm điều gì đó với JDialog và các JButtons, bạn công việc sẽ chỉ để setText ("kết quả từ Comparator")

  • trong trường hợp ArrayList của bạn cho JTextField AutoComplete đã được sắp xếp, sau đó bạn có hai cú hích

    a) khuynh hướng ghi đè dễ nhất từ ​​tính tự động hoàn thành AutoComplete bằng cách thêm mảng riêng biệt cho setText() cho 6/9/max 12 butto ns trên popup JDialog, nếu bạn setBackground (Color.white), sau đó bạn không quan tâm bằng cách nào đó để ẩn JButtons mà không cần văn bản

    b) một cách khác có thể là tạo Comparator riêng để tìm kiếm (cùng một tính năng AutoComplete) đầu tiên 6/9/tối đa 12 trận đấu,

  • để chụp một sự kiện từ 6/9/tối đa 12 JButtons sử dụng putClientProperty hoặc EventHandler hoặc Swing Actions, nơi bạn chỉ để kiểm tra xem văn bản isEmpty :-),

  • lẽ Swing Actions có thể là cách tốt nhất vì các sự kiện của nó có thể sảy ra và bạn có thể enabled/disable (nếu JButtons t ext isEmpty) đầu ra từ Hành động này theo mặc định

Các vấn đề liên quan