Đ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.
- chính ép
- 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ự)
- cư JTable với những entires
- Set RowFilter với regex dựa trên JTextField nhập để lọc thông qua dữ liệu lấy
- 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();
}
}
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
@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'. –
@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