筛选JComboBox [英] Filtering JComboBox

查看:88
本文介绍了筛选JComboBox的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在开始时,我会说我不介意自动完成组合框,而是在我的组合框中有一个"setFilter(Set)"方法,因此它将显示组合中的内容.

At beginning I will say that I don't have in mind auto-complete combobox, but rather having a "setFilter(Set)" method in my combobox, so it displays what is in the set.

我无法达到那种效果,尝试使用不同的方法,我认为过滤显示的内容是视图的责任,所以我不应该扩展ComboBoxModel.

I was unable to achieve that effect, trying different approaches and I think it's the view responsibility to filter what it displays, so I should not extend ComboBoxModel.

这是我到目前为止所拥有的(主要包括不起作用的情况):

This is what I have so far (main includes the case which doesn't work):

import java.awt.*;
import java.util.Set;

import javax.swing.*;


public class FilteredComboBox extends JComboBox {
    private ComboBoxModel entireModel;
    private final DefaultComboBoxModel filteredModel = new DefaultComboBoxModel();

    private Set objectsToShow;

    public FilteredComboBox(ComboBoxModel model) {
        super(model);
        this.entireModel = model;
    }

    public void setFilter(Set objectsToShow) {
        if (objectsToShow != null) {
            this.objectsToShow = objectsToShow;
            filterModel();
        } else {
            removeFilter();
        }
    }

    public void removeFilter() {
        objectsToShow = null;
        filteredModel.removeAllElements();
        super.setModel(entireModel);
    }

    private void filterModel() {
        filteredModel.removeAllElements();
        for (int i = 0; i < entireModel.getSize(); ++i) {
            Object element = entireModel.getElementAt(i);
            addToFilteredModelIfShouldBeDisplayed(element);
        }

        super.setModel(filteredModel);
    }

    private void addToFilteredModelIfShouldBeDisplayed(Object element) {
        if (objectsToShow.contains(element)) {
            filteredModel.addElement(element);
        }
    }

    @Override
    public void setModel(ComboBoxModel model) {
        entireModel = model;
        super.setModel(entireModel);
        if (objectsToShow != null) {
            filterModel();
        }
    }

    public static void main(String[] args) {
        JFrame f = new JFrame();
        f.setLayout(new BoxLayout(f.getContentPane(), BoxLayout.X_AXIS));
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        DefaultComboBoxModel model = new DefaultComboBoxModel();

        FilteredComboBox cb = new FilteredComboBox(model);
        cb.setPrototypeDisplayValue("XXXXXXXXXXXX");

        f.add(cb);
        f.pack();

        Set objectsToShow = new HashSet();
        objectsToShow.add("1");
        objectsToShow.add("3");
        objectsToShow.add("4");

        cb.setFilter(objectsToShow); // if you set that filter after addElements it will work
        model.addElement("1");
        model.addElement("2");
        model.addElement("3");
        model.addElement("4");
        model.addElement("5");

        f.setVisible(true);
    }
}

推荐答案

我认为过滤显示的内容是视图的责任" -我认为,该视图显示所告诉的内容,模型会驱动它可以显示的内容,但这就是我...

"I think it's the view responsibility to filter what it displays" - I'd argue that, the view displays what it's told, the model drives what it can show, but that's me...

这是我在Java 1.3中编写的想法(具有通用更新),该想法基本上将代理ComboBoxModel包裹在另一个ComboBoxModel周围.然后,代理服务器(或FilterableComboBoxModel)决定原始模型中的哪些元素与过滤器匹配并更新其索引.

This is an idea I wrote way back in Java 1.3 (with generic updates) which basically wraps a proxy ComboBoxModel around another ComboBoxModel. The proxy (or FilterableComboBoxModel) then makes decisions about which elements from the original model match a filter and updates it's indices.

基本上,它所做的只是在自身和原始模型之间生成索引映射,因此它不会复制任何内容或生成对原始数据的新引用.

Basically, all it does is generates an index map between itself and the original model, so it's not copying anything or generating new references to the original data.

通过可过滤"接口控制过滤,该接口仅传递要检查的元素,并期望得到boolean结果作为响应.这使API具有高度的灵活性,因为可以按照您希望的任何方式进行过滤,而无需以任何方式更改FilterableComboBoxModel.这也意味着您可以通过简单地应用一个新的过滤器来更改使用的过滤器.

The filtering is controlled via a "filterable" interface which simply passes the element to be checked and expects a boolean result in response. This makes the API highly flexible as filtering can be done any way you want without the need to change the FilterableComboBoxModel in any way. It also means you can change the filter been used by simply applying a new one...

如果您像往常一样希望向过滤器传递一些值,则需要通过updateFilter方法通知模型过滤器已更改...是的,我知道,可能是一个更好的主意,但我试图使其保持简单;)

If, like I usually do, you want to pass some value to the filter, you will need to inform the model that the filter has changed, via the updateFilter method...yeah, I know, a ChangeListener would probably be a better idea, but I was trying to keep it simple ;)

为了灵活性(并维护当前继承模型),核心API基于ListModel,这意味着,您也可以将相同的概念与JList

For flexibility (and to maintain the current inheritance model), the core API is based on a ListModel, which means, you can also use the same concept with a JList

import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractListModel;
import javax.swing.ListModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;

public class FilterableListModel<E> extends AbstractListModel<E> implements ListDataListener {

    private ListModel<E> peer;
    private List<Integer> lstFilteredIndicies;
    private IFilterable filter;

    public FilterableListModel() {
        lstFilteredIndicies = new ArrayList<Integer>(25);
    }

    public FilterableListModel(ListModel<E> model) {
        this();
        setModel(model);
    }

    public FilterableListModel(ListModel<E> model, IFilterable filter) {
        this();

        setModel(model);
        setFilter(filter);
    }

    public void setModel(ListModel<E> parent) {
        if (peer == null || !peer.equals(parent)) {
            if (peer != null) {
                fireIntervalRemoved(this, 0, peer.getSize() - 1);
                peer.removeListDataListener(this);
            }

            peer = parent;
            lstFilteredIndicies.clear();
            if (peer != null) {
                peer.addListDataListener(this);
            }
            filterModel(true);
        }
    }

    public ListModel<E> getModel() {
        return peer;
    }

    @Override
    public int getSize() {
        IFilterable filter = getFilter();
        return filter == null ? getModel() == null ? 0 : getModel().getSize() : lstFilteredIndicies.size();
    }

    @Override
    public E getElementAt(int index) {
        IFilterable filter = getFilter();
        ListModel<E> model = getModel();

        E value = null;
        if (filter == null) {
            if (model != null) {
                value = model.getElementAt(index);
            }
        } else {
            int filterIndex = lstFilteredIndicies.get(index);
            value = model.getElementAt(filterIndex);
        }
        return value;
    }

    public int indexOf(Object value) {
        int index = -1;
        for (int loop = 0; loop < getSize(); loop++) {
            Object at = getElementAt(loop);
            if (at == value) {
                index = loop;
                break;
            }
        }
        return index;
    }

    @Override
    public void intervalAdded(ListDataEvent e) {
        IFilterable filter = getFilter();
        ListModel model = getModel();

        if (model != null) {
            if (filter != null) {
                int startIndex = Math.min(e.getIndex0(), e.getIndex1());
                int endIndex = Math.max(e.getIndex0(), e.getIndex1());
                for (int index = startIndex; index <= endIndex; index++) {
                    Object value = model.getElementAt(index);
                    if (filter.include(value)) {
                        lstFilteredIndicies.add(index);
                        int modelIndex = lstFilteredIndicies.indexOf(index);
                        fireIntervalAdded(this, modelIndex, modelIndex);
                    }
                }
            } else {
                fireIntervalAdded(this, e.getIndex0(), e.getIndex1());
            }
        }
    }

    @Override
    public void intervalRemoved(ListDataEvent e) {
        IFilterable filter = getFilter();
        ListModel model = getModel();

        if (model != null) {
            if (filter != null) {
                int oldRange = lstFilteredIndicies.size();
                filterModel(false);
                fireIntervalRemoved(this, 0, oldRange);
                if (lstFilteredIndicies.size() > 0) {
                    fireIntervalAdded(this, 0, lstFilteredIndicies.size());
                }
            } else {
                fireIntervalRemoved(this, e.getIndex0(), e.getIndex1());
            }
        }
    }

    @Override
    public void contentsChanged(ListDataEvent e) {
        filterModel(true);
    }

    public void setFilter(IFilterable<E> value) {
        if (filter == null || !filter.equals(value)) {
            filter = value;
            if (getModel() != null) {
                if (getModel().getSize() > 0) {
                    fireIntervalRemoved(this, 0, getModel().getSize() - 1);
                }
            }
            filterModel(true);
        }
    }

    public IFilterable<E> getFilter() {
        return filter;
    }

    protected void filterModel(boolean fireEvent) {
        if (getSize() > 0 && fireEvent) {
            fireIntervalRemoved(this, 0, getSize() - 1);
        }
        lstFilteredIndicies.clear();

        IFilterable<E> filter = getFilter();
        ListModel<E> model = getModel();
        if (filter != null && model != null) {
            for (int index = 0; index < model.getSize(); index++) {
                E value = model.getElementAt(index);
                if (filter.include(value)) {
                    lstFilteredIndicies.add(index);
                    if (fireEvent) {
                        fireIntervalAdded(this, getSize() - 1, getSize() - 1);
                    }
                }
            }
        }
    }

    public void updateFilter() {
        IFilterable filter = getFilter();
        ListModel model = getModel();

        if (filter != null && model != null) {
            for (int index = 0; index < model.getSize(); index++) {
                Object value = model.getElementAt(index);
                if (filter.include(value)) {
                    if (!lstFilteredIndicies.contains(index)) {
                        lstFilteredIndicies.add(index);
                        fireIntervalAdded(this, getSize() - 1, getSize() - 1);
                    }
                } else if (lstFilteredIndicies.contains(index)) {
                    int oldIndex = lstFilteredIndicies.indexOf(index);
                    lstFilteredIndicies.remove(oldIndex);
                    fireIntervalRemoved(this, oldIndex, oldIndex);
                }
            }
        }
    }
}

可过滤

public interface IFilterable<O> {

    public boolean include(O value);

}

FilterableComboBoxModel

import javax.swing.ComboBoxModel;

public class FilterableComboBoxModel<E> extends FilterableListModel<E> implements ComboBoxModel<E> {

    private FilterableComboBoxModel(ComboBoxModel<E> model) {
        super(model);
    }

    public ComboBoxModel<E> getComboBoxModel() {
        return (ComboBoxModel) getModel();
    }

    @Override
    public void setSelectedItem(Object anItem) {
        getComboBoxModel().setSelectedItem(anItem);
    }

    @Override
    public Object getSelectedItem() {
        return getComboBoxModel().getSelectedItem();
    }

}

应注意,实际上可能有可能使用

It should be noted that it might actually be possible to use a RowFilter instead, but I've never really had the time to look at it (since I already had a working API)

这篇关于筛选JComboBox的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆