Фильтрация JList на основе JTextField
У меня есть JTextField и JList в моей программе. JList содержит контакты Пользователя. Я хотел бы отфильтровать JList на основе текста в JTextField. Например, если я введу "Майк", он будет показывать только контакты, включая"Майк". Когда пользователь очищает JTextField, он сбрасывает фильтр.
Я знаю, что могу сделать это вручную, имея два массива. Один для исходных контактов и один для отфильтрованных. Когда пользователь изменяет значение JTextField, я бы пошел trought исходный список, обновить временный список и обновить JList. Мне просто интересно, есть ли какая-то встроенная функция, чтобы избежать ручного труда.
3 ответов
лучший способ сделать такие вещи - иметь реализацию ListModel, которая фильтрует ее содержимое. Я не знаю никаких реализаций ListModel фильтрации по умолчанию, но это не должно быть слишком сложно сделать. Вот быстрое и грязное решение, чтобы дать вам идею. Возможно, вы захотите добавить больше колокольчиков и свистков.
package test;
import java.util.ArrayList;
import javax.swing.AbstractListModel;
import javax.swing.ListModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
public class FilteredListModel extends AbstractListModel {
public static interface Filter {
boolean accept(Object element);
}
private final ListModel _source;
private Filter _filter;
private final ArrayList<Integer> _indices = new ArrayList<Integer>();
public FilteredListModel(ListModel source) {
if (source == null)
throw new IllegalArgumentException("Source is null");
_source = source;
_source.addListDataListener(new ListDataListener() {
public void intervalRemoved(ListDataEvent e) {
doFilter();
}
public void intervalAdded(ListDataEvent e) {
doFilter();
}
public void contentsChanged(ListDataEvent e) {
doFilter();
}
});
}
public void setFilter(Filter f) {
_filter = f;
doFilter();
}
private void doFilter() {
_indices.clear();
Filter f = _filter;
if (f != null) {
int count = _source.getSize();
for (int i = 0; i < count; i++) {
Object element = _source.getElementAt(i);
if (f.accept(element)) {
_indices.add(i);
}
}
fireContentsChanged(this, 0, getSize() - 1);
}
}
public int getSize() {
return (_filter != null) ? _indices.size() : _source.getSize();
}
public Object getElementAt(int index) {
return (_filter != null) ? _source.getElementAt(_indices.get(index)) : _source.getElementAt(index);
}
}
чтобы использовать его, вам нужно установить его в свой JList, а затем вызвать setFilter() по мере необходимости. Вот пример:
ListModel source = new DefaultListModel(); // use a model of your choice here;
FilteredListModel filteredListModel = new FilteredListModel(source);
JList list = new JList(filteredListModel);
filteredListModel.setFilter(new FilteredListModel.Filter() {
public boolean accept(Object element) {
return false; // put your filtering logic here.
}
});
раз метод setFilter () вызывается ваш JList на экране, как ожидается, соответствующим образом изменит его содержимое.
кроме того, вы можете реализовать шаблон observer/observable для своего фильтра, чтобы вы могли повторно фильтровать список без вызова метода setFilter(). Вы можете поэкспериментировать с этим позже. Для первой итерации это достаточно хорошо, если вы вызываете метод setFilter каждый раз, когда пользователь вводит что-то в вашем JTextField.
более простым решением может быть использование JTable
, который имеет встроенную способность фильтровать и сортировать (RowSorter
). Таблица с одним столбцом не слишком отличается от списка.
Если вы в порядке с внешними библиотеками, я бы рекомендовал QuickListFilterField/QuickTreeFilterField Jide. С несколькими строками кода Вы можете получить визуально фильтруемый JList/JTree, чувствительный к регистру/нечувствительный поиск, подстановочный знак / регулярное выражение и т. д... Удивительно проста в использовании !