简体   繁体   English

基于JTextField过滤JList

[英]Filtering JList based on JTextField

I have a JTextField and a JList in my program. 我的程序中有一个JTextField和一个JList。 The JList contains the user's contacts. JList包含用户的联系人。 I'd like to filter the JList based on the text on the JTextField. 我想根据JTextField上的文本过滤JList。 For example, if I type in "Mike" it will only show contacts including "Mike". 例如,如果我输入“Mike”,它将只显示包含“Mike”的联系人。 When the user clears the JTextField it would reset the filter. 当用户清除JTextField时,它将重置过滤器。

I know I could do this manually by having two arrays. 我知道我可以通过两个数组手动完成此操作。 One for the original contacts and one for the filtered ones. 一个用于原始联系人,另一个用于过滤联系人。 When the user changes the value of the JTextField I would go trought the original list, update the temporary list and update the JList. 当用户更改JTextField的值时,我将使用原始列表,更新临时列表并更新JList。 I just wonder if there is some built in feature to avoid manual labour. 我只是想知道是否有一些内置功能可以避免手工劳动。

The best way to do things like that is to have a ListModel implementation that filters its contents. 执行此类操作的最佳方法是使用ListModel实现来过滤其内容。 I don't know of any default filtering ListModel implementations, but it should not be too hard to do. 我不知道任何默认过滤ListModel实现,但它不应该太难。 Here's a quick and dirty solution just to give you an idea. 这是一个快速而肮脏的解决方案,只是为了给你一个想法。 You might want to add more bells and whistles to it. 你可能想要添加更多铃声和口哨声。

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);
    }
}

In order to use it you need to set it to your JList and then call setFilter() as you need. 要使用它,您需要将其设置为JList,然后根据需要调用setFilter()。 Here's an example: 这是一个例子:

    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.
        }
    });

Once method setFilter() is invoked your JList on the screen is expected to change its contents accordingly. 一旦调用了方法setFilter(),屏幕上的JList就会相应地改变其内容。

Alternatively, you may want to implement an observer/observable pattern for your Filter, so you can re-filter the list without calling method setFilter(). 或者,您可能希望为Filter实现一个观察者/可观察模式,因此您可以重新过滤列表而无需调用方法setFilter()。 You can experiment with that later. 您可以稍后进行试验。 For the first iteration it's good enough as long as you call method setFilter every time user types something in your JTextField. 对于第一次迭代,只要每次用户在JTextField中键入内容时调用方法setFilter,它就足够了。

A simpler solution might be to use JTable , which does have a built-in ability to filter and sort ( RowSorter ). 一个更简单的解决方案可能是使用JTable ,它具有内置的过滤和排序功能( RowSorter )。 A single-column table is not too different from a list. 单列表与列表没有太大区别。

If you're okay with external libs, I would recommend Jide's QuickListFilterField/QuickTreeFilterField. 如果你对外部库有好处,我会推荐Jide的QuickListFilterField / QuickTreeFilterField。 With few lines of code, you could get a visually filterable JList/JTree, case sensitive/insensitive search, wildcard/regex matching etc ... Amazingly easy to use ! 只需几行代码,您就可以获得视觉上可过滤的JList / JTree,区分大小写/不区分大小写的搜索,通配符/正则表达式匹配等......非常容易使用!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM