过滤JComboBox

一开始我会说我没有考虑自动完成combobox,而是在我的combobox中有一个“setFilter(Set)”方法,所以它显示了集合中的内容。

我无法实现这种效果,尝试不同的方法,我认为过滤它显示的内容是视图的责任,所以我不应该扩展ComboBoxModel。

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

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

“我认为过滤它显示的东西是视图的责任” – 我认为,视图显示它所说的内容,模型驱动它可以显示的内容,但那就是我……

这是我在Java 1.3(带有通用更新)中编写的一个想法,它基本上将代理ComboBoxModel包装在另一个ComboBoxModel周围。 然后,代理(或FilterableComboBoxModel )决定原始模型中的哪些元素与filter匹配并更新其索引。

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

过滤通过“可过滤”接口控制,该接口简单地传递要检查的元素并期望响应中的boolean结果。 这使得API非常灵活,因为可以以任何方式进行FilterableComboBoxModel ,而无需以任何方式更改FilterableComboBoxModel 。 这也意味着您可以通过简单地应用新的filter来更改已使用的filter…

如果像我通常那样想要将一些值传递给filter,则需要通过updateFilter方法通知模型filter已经更改…是的,我知道, ChangeListener可能是个更好的主意,但我试图保持简单;)

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

FilterableListModel

 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 extends AbstractListModel implements ListDataListener { private ListModel peer; private List lstFilteredIndicies; private IFilterable filter; public FilterableListModel() { lstFilteredIndicies = new ArrayList(25); } public FilterableListModel(ListModel model) { this(); setModel(model); } public FilterableListModel(ListModel model, IFilterable filter) { this(); setModel(model); setFilter(filter); } public void setModel(ListModel 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 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 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 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 getFilter() { return filter; } protected void filterModel(boolean fireEvent) { if (getSize() > 0 && fireEvent) { fireIntervalRemoved(this, 0, getSize() - 1); } lstFilteredIndicies.clear(); IFilterable filter = getFilter(); ListModel 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 { public boolean include(O value); } 

FilterableComboBoxModel

 import javax.swing.ComboBoxModel; public class FilterableComboBoxModel extends FilterableListModel implements ComboBoxModel { private FilterableComboBoxModel(ComboBoxModel model) { super(model); } public ComboBoxModel getComboBoxModel() { return (ComboBoxModel) getModel(); } @Override public void setSelectedItem(Object anItem) { getComboBoxModel().setSelectedItem(anItem); } @Override public Object getSelectedItem() { return getComboBoxModel().getSelectedItem(); } } 

应该注意的是,实际上可能会使用RowFilter ,但我从来没有真正有时间去查看它(因为我已经有了一个有效的API)