可模糊匹配下拉框

public class FilterableComboBox<T> extends JComboBox
{
    private interface Filter
    {
        public boolean accept(Object obj);
    }
//去掉:表格中显示问题
//    public void setItem(final Object item)
//    {
//        super.setSelectedItem(item);
//    }
//
//    @Override
//    public void setSelectedItem(final Object item)
//    {
//        if (item != null)
//        {
//            setSelectedItem(item.toString());
//            return;
//        }
//
//        super.setSelectedItem(item);
//    }
//
//    public void setSelectedItem(final String item)
//    {
//        super.setSelectedItem(getFilterItem(item));
//    }

    private class FilterableComboBoxModel extends AbstractListModel implements MutableComboBoxModel
    {
        private static final long serialVersionUID = -8893609766963132266L;

        private T selectedItem;

        public FilterableComboBoxModel(final List<T> items)
        {
            this.addElementList(items);
            FilterableComboBox.this.isInitialed = true;
        }

        @SuppressWarnings("unused")
        public FilterableComboBoxModel(final T[] items)
        {
            this.addElementList(items);
            FilterableComboBox.this.isInitialed = true;
        }

        @Override
        @SuppressWarnings("unchecked")
        public void addElement(final Object item)
        {
            FilterableComboBox.this.allItems.add((T) item);
            this.updateAddedItem((T) item);
        }

        @SuppressWarnings("unused")
        public void addElement(final T[] items)
        {
            if ((items == null) || (items.length == 0))
            {
                return;
            }
            for (T tmp : items)
            {
                FilterableComboBox.this.allItems.add(tmp);
            }
            this.updateAddedItems(items);
        }

        private void addElementList(final Collection<T> items)
        {
            if ((items == null) || (items.size() == 0))
            {
                return;
            }
            FilterableComboBox.this.allItems.addAll(items);
            this.updateAddedItems(items);
        }

        private void addElementList(final T[] items)
        {
            if ((items == null) || (items.length == 0))
            {
                return;
            }
            for (T tmp : items)
            {
                FilterableComboBox.this.allItems.add(tmp);
            }
            this.updateAddedItems(items);
        }

        @Override
        public void fireIntervalAdded(final Object source, final int index0, final int index1)
        {
            super.fireIntervalAdded(source, index0, index1);
        }

        @Override
        public void fireIntervalRemoved(final Object source, final int index0, final int index1)
        {
            super.fireIntervalRemoved(source, index0, index1);
        }

        @Override
        public Object getElementAt(final int index)
        {
            if (index < 0)
            {
                return null;
            }

            if (FilterableComboBox.this.filteredItems.size() <= index)
            {
                return null;
            }

            return FilterableComboBox.this.filteredItems.get(index);
        }

        @Override
        public T getSelectedItem()
        {
            if ((this.selectedItem == null) || (this.selectedItem.toString().isEmpty()))
            {
                return null;
            }

            return this.selectedItem;
        }

        @Override
        public int getSize()
        {
            return FilterableComboBox.this.filteredItems.size();
        }

        @Override
        public void insertElementAt(final Object obj, final int index)
        {
            throw new UnsupportedOperationException("Operation 'insertElementAt' Not supported!");
        }

        @Override
        @SuppressWarnings("unchecked")
        public void removeElement(final Object item)
        {
            if (item == null)
            {
                return;
            }
            int index = FilterableComboBox.this.filteredItems.indexOf(item);
            if (index < 0)
            {
                return;
            }

            this.removeOneElement((T) item, index);
        }

        @Override
        public void removeElementAt(final int index)
        {
            if (index < 0)
            {
                return;
            }

            T item = FilterableComboBox.this.allItems.get(index);

            if (item == null)
            {
                return;
            }

            this.removeOneElement(item, index);
        }

        private synchronized void removeOneElement(final T item, final int index)
        {
            FilterableComboBox.this.allItems.remove(item);
            FilterableComboBox.this.filteredItems.remove(index);

            if (item == this.selectedItem)
            {
                if (index > 0)
                {
                    this.selectedItem = FilterableComboBox.this.filteredItems.get(index - 1);
                    FilterableComboBox.this.cmbEditor.setItem(this.selectedItem);
                }
                else
                {
                    this.selectedItem = null;
                    FilterableComboBox.this.cmbEditor.setItem(null);
                }
            }
            this.updateRemovedItem(item);
        }

        private T searchExact()
        {
            T selectedItem = null;
            if (this.selectedItem == null)
            {
                for (T item : FilterableComboBox.this.filteredItems)
                {
                    if (item.toString().indexOf(FilterableComboBox.this.orginStr) >= 0)
                    {
                        selectedItem = item;
                        break;
                    }
                }
            }
            else
            {
                if (this.selectedItem.toString().indexOf(FilterableComboBox.this.orginStr) < 0)
                {
                    for (T item : FilterableComboBox.this.filteredItems)
                    {
                        if (item.toString().indexOf(FilterableComboBox.this.orginStr) >= 0)
                        {
                            selectedItem = item;
                            break;
                        }
                    }
                }
                else
                {
                    selectedItem = this.selectedItem;
                }
            }

            if ((selectedItem == null) && (FilterableComboBox.this.filteredItems.size() > 0))
            {
                selectedItem = FilterableComboBox.this.filteredItems.get(0);
            }
            return selectedItem;
        }

        private void searchIgnoreCase()
        {
            for (T item : FilterableComboBox.this.allItems)
            {
                if (FilterableComboBox.this.filter.accept(item))
                {
                    FilterableComboBox.this.filteredItems.add(item);
                }
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void setSelectedItem(final Object item)
        {
            if (!allowedMismatch && filtering)
            {
                if (this.selectedItem == null)
                {
                    text.setText("");
                    return;
                }
            }

            this.setSelectedObject((T) item);
            this.fireContentsChanged(this, -1, -1);
        }

        private void setSelectedObject(final T item)
        {
            if ((this.selectedItem == null) && (item == null))
            {
                return;
            }

            if ((this.selectedItem != null) && this.selectedItem.toString().equals(item))
            {
                return;
            }

            if ((item != null) && item.toString().equals(this.selectedItem))
            {
                return;
            }

            this.selectedItem = item;
        }

        private void updateAddedItem(final T item)
        {
            if (FilterableComboBox.this.isInitialed == false)
            {
                FilterableComboBox.this.filteredItems.add(item);
            }
            else
            {
                this.updateFilteredItems();
            }
        }

        private void updateAddedItems(final Collection<T> items)
        {
            if (FilterableComboBox.this.isInitialed == false)
            {
                FilterableComboBox.this.filteredItems.addAll(items);
            }
            else
            {
                this.updateFilteredItems();
            }
        }

        private void updateAddedItems(final T[] items)
        {
            if (FilterableComboBox.this.isInitialed == false)
            {
                for (T item : items)
                {
                    FilterableComboBox.this.filteredItems.add(item);
                }
            }
            else
            {
                this.updateFilteredItems();
            }
        }

        private void updateFilteredItems()
        {
            if (FilterableComboBox.this.freezeFilter)
            {
                return;
            }

            T selectedItem = null;
            FilterableComboBox.this.filteredItems.clear();

            if (FilterableComboBox.this.filter == null)
            {
                FilterableComboBox.this.filteredItems.addAll(FilterableComboBox.this.allItems);
            }
            else
            {
                this.searchIgnoreCase();
                if ((FilterableComboBox.this.filteredItems.size() > 0) && (FilterableComboBox.this.orginStr != null))
                {
                    selectedItem = this.searchExact();
                }
            }

            if (FilterableComboBox.this.oldItem != selectedItem)
            {
                this.setSelectedObject(selectedItem);
                FilterableComboBox.this.changed = true;
            }
            else
            {
                FilterableComboBox.this.changed = false;
            }

            // TODO:这里的算法需要重新整理
            if (selectedItem == null)
            {
                if (FilterableComboBox.this.cmbEditor.getItem().toString().length() > 0)
                {
                    FilterableComboBox.this.changed = true;
                }
                if (FilterableComboBox.this.filter != null)
                {
                    FilterableComboBox.this.filteredItems.addAll(FilterableComboBox.this.allItems);
                }
                else
                {

                }
                if ((FilterableComboBox.this.oldItem != FilterableComboBox.this.defaultItem)
                        && (FilterableComboBox.this.defaultItem != null))
                {
                    this.setSelectedItem(FilterableComboBox.this.defaultItem);
                    FilterableComboBox.this.changed = true;
                }
                else
                {
                    FilterableComboBox.this.changed = false;
                }
            }
            else
            {
                if (!FilterableComboBox.this.cmbEditor.getItem().toString().equals(selectedItem.toString()))
                {
                    FilterableComboBox.this.changed = true;
                }
            }

            FilterableComboBox.this.updateComboBox();
        }

        private void updateRemovedItem(final T item)
        {
            if (FilterableComboBox.this.isInitialed == false)
            {
                this.updateFilteredItems();
            }
        }
    }

    protected JTextField getTextEditor()
    {
        return new JTextField(32);
    }

    protected class FilterableEditor extends BasicComboBoxEditor implements ComboBoxEditor
    {
        public FilterableEditor()
        {
            FilterableComboBox.this.text = getTextEditor();
            // FilterableComboBox.this.text.setBorder(new EmptyBorder(1, 1, 1, 1));

            FilterableComboBox.this.text.addKeyListener(FilterableComboBox.this.keyListener);

            FilterableComboBox.this.text.addFocusListener(FilterableComboBox.this.focusListener);

            FilterableComboBox.this.text.getDocument().addDocumentListener(FilterableComboBox.this.documentListener);
        }

        @Override
        public void addActionListener(final ActionListener l)
        {
            FilterableComboBox.this.text.addActionListener(l);
        }

        @Override
        public Component getEditorComponent()
        {
            return FilterableComboBox.this.text;
        }

        @Override
        public Object getItem()
        {
            return FilterableComboBox.this.text.getText();
        }

        @Override
        public void removeActionListener(final ActionListener l)
        {
            FilterableComboBox.this.text.removeActionListener(l);
        }

        @Override
        public void selectAll()
        {
            FilterableComboBox.this.text.selectAll();
        }

        @Override
        public void setItem(final Object item)
        {
            if (FilterableComboBox.this.freezeFilter)
            {
                this.setItemValue(item);
                return;
            }
            if (FilterableComboBox.this.filtering)
            {
                return;
            }

            // 如果下拉框选项正在改变则不过滤
            // if (!changed)
            // {
            FilterableComboBox.this.setting = true;
            this.setItemValue(item);
            FilterableComboBox.this.setting = false;
            // }
        }

        private void setItemValue(final Object item)
        {
            String newText = (item == null) ? "" : item.toString();
            FilterableComboBox.this.text.setText(newText);
        }

    }

    private class StartsWithFilter implements Filter
    {
        private final String filterStr;

        public StartsWithFilter(final String prefix)
        {
            this.filterStr = prefix.toLowerCase();
        }

        @Override
        public boolean accept(final Object o)
        {
            return o.toString().toLowerCase().indexOf(this.filterStr) >= 0;
        }
    }

    /**
     * 所有下拉菜单项
     */
    private final List<T> allItems = new ArrayList<T>();

    /**
     * 已经过滤出来的下拉菜单项
     */
    private final List<T> filteredItems = new ArrayList<T>();

    protected FilterableComboBoxModel cmbModel = new FilterableComboBoxModel(this.filteredItems);

    /**
     * 编辑器
     */
    protected transient BasicComboBoxEditor cmbEditor = null;;

    /**
     * 过滤器
     */
    private Filter filter = null;

    /**
     * 正在过滤
     */
    protected volatile boolean filtering = false;

    /**
     * 正在设值
     */
    protected volatile boolean setting = false;

    /**
     * 禁止过滤
     */
    protected boolean freezeFilter = false;

    protected JTextField text = null;

    /**
     * 是否已经初始化,如果为false,则下拉框禁用过滤等等监听事件,在下拉框初始化大数据时有用
     */
    private boolean isInitialed = false;

    /**
     * 下拉框滚动条一页显示的数据个数
     */
    private static final int MAXROWCOUNT = 8;

    private volatile T defaultItem = null;

    private boolean bItemListenerAdded = false;

    private volatile Object oldItem = null;

    private volatile boolean changed = false;

    /**
     * 是否允许不匹配的项
     */
    private boolean allowedMismatch = true;

    protected transient FocusListener focusListener = new FocusAdapter()
    {
        @Override
        public void focusGained(final FocusEvent e)
        {
            FilterableComboBox.this.text.selectAll();
            FilterableComboBox.this.oldItem = FilterableComboBox.this.cmbModel.getSelectedItem();
        }

        @Override
        public void focusLost(final FocusEvent e)
        {
            if (FilterableComboBox.this.freezeFilter)
            {
                return;
            }
            Object item = FilterableComboBox.this.cmbModel.getSelectedItem();
            if ((item == null) && (!allowedMismatch))
            {
                text.setText("");
            }
            else
            {
                FilterableComboBox.this.changeSelect(item);
            }
            if (FilterableComboBox.this.filteredItems.size() == 0)
            {
                FilterableComboBox.this.reloadComboBox();
            }
        }
    };

    /**
     * JComboBoxcellrenderer.
     */
    private DefaultListCellRenderer listCellRenderer = new DefaultListCellRenderer()
    {
        private static final long serialVersionUID = -7330621790373229915L;

        // 重写它的方法:
        @Override
        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
                boolean cellHasFocus)
        {
            if (value == null)
            {
                return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            }

            list.setToolTipText(value.toString());

            return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
        }
    };

    protected transient KeyListener keyListener = new KeyAdapter()
    {
        @Override
        public void keyPressed(final KeyEvent e)
        {
            if (e.getKeyCode() == KeyEvent.VK_F4)
            {
                FilterableComboBox.this.setPopupVisible(!FilterableComboBox.this.isPopupVisible());
                FilterableComboBox.this.text.selectAll();
                return;
            }
            if (FilterableComboBox.this.freezeFilter)
            {
                return;
            }
            Object item = FilterableComboBox.this.cmbModel.getSelectedItem();
            if ((e.getKeyCode() == KeyEvent.VK_ENTER) || (e.getKeyCode() == KeyEvent.VK_TAB))
            {
                FilterableComboBox.this.changeSelect(item);
                if (FilterableComboBox.this.filteredItems.size() == 0)
                {
                    FilterableComboBox.this.reloadComboBox();
                }
            }
        }

        @Override
        public void keyReleased(final KeyEvent e)
        {
            if ((e.getKeyCode() == KeyEvent.VK_ENTER) && (FilterableComboBox.this.cmbModel.getSelectedItem() == null))
            {
                try
                {
                    FilterableComboBox.this.setPopupVisible(true);
                }
                catch (Exception ex)
                {
                }
            }
        }
    };

    protected DocumentListener documentListener = new DocumentListener()
    {
        @Override
        public void changedUpdate(final DocumentEvent e)
        {
        }

        @Override
        public void insertUpdate(final DocumentEvent e)
        {
            try
            {
                FilterableComboBox.this.handleChange();
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
        }

        @Override
        public void removeUpdate(final DocumentEvent e)
        {
            try
            {
                FilterableComboBox.this.handleChange();
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
        }
    };

    private String orginStr = null;

    private final ItemListener itemListener = new ItemListener()
    {
        @Override
        public void itemStateChanged(final ItemEvent e)
        {
            if (e.getStateChange() == ItemEvent.SELECTED)
            {
                FilterableComboBox.this.fireActionPerformed();
            }
        }
    };

    public FilterableComboBox()
    {
        this.init();
        this.setEditable(true);
    }

    // @Override
    // public boolean requestFocus(boolean temporary)
    // {
    // super.requestFocus(temporary);
    // this.text.selectAll();
    // return this.text.requestFocus(temporary);
    // }
    //
    // @Override
    // public boolean requestFocusInWindow(boolean temporary)
    // {
    // super.requestFocusInWindow(temporary);
    // this.text.selectAll();
    // return this.text.requestFocus(temporary);
    // }
    //
    // @Override
    // public void requestFocus()
    // {
    // super.requestFocus();
    // this.text.requestFocus();
    // this.text.selectAll();
    // }
    //
    // @Override
    // public boolean requestFocusInWindow()
    // {
    // super.requestFocusInWindow();
    // this.text.selectAll();
    // return this.text.requestFocusInWindow();
    // }

    public FilterableComboBox(final BasicComboBoxEditor editor)
    {
        this.setModel(this.cmbModel);

        this.cmbEditor = editor;
        this.text = (JTextField) this.cmbEditor.getEditorComponent();
        // this.text.setBorder(BorderFactory.createEmptyBorder());
        this.text.addFocusListener(this.focusListener);
        this.addKeyListener(this.keyListener);
        this.text.addKeyListener(this.keyListener);
        this.text.getDocument().addDocumentListener(this.documentListener);
        this.setEditor(this.cmbEditor);
        this.setRenderer(this.listCellRenderer);
        this.setEditable(true);
    }

    public FilterableComboBox(final List<T> items)
    {
        this.init();
        this.addItems(items);
        this.setEditable(true);
        this.isInitialed = true;
    }

    public FilterableComboBox(final T[] items)
    {
        this.init();
        this.addItems(items);
        this.setEditable(true);
        this.isInitialed = true;
    }

    @Override
    public void addActionListener(final ActionListener l)
    {
        if (!this.freezeFilter && !this.bItemListenerAdded)
        {
            this.addItemListener(this.itemListener);
            this.bItemListenerAdded = true;
        }
        super.addActionListener(l);
    }

    /**
     * @see javax.swing.JComboBox#addItem(java.lang.Object)
     */
    @Override
    public void addItem(final Object item)
    {
        this.addItem(item, false);
    }

    public void addItem(final Object item, final boolean isCheck)
    {
        if (isCheck)
        {
            if (!this.allItems.contains(item))
            {
                this.cmbModel.addElement(item);
            }
        }
        else
        {
            this.cmbModel.addElement(item);
        }

        if (this.isInitialed == false)
        {
            this.updateComboBox();
        }
    }

    public void addItems(final Collection<T> items)
    {
        this.addItems(items, false);
    }

    public void addItems(final Collection<T> items, final boolean isCheck)
    {
        if ((items == null) || (items.size() == 0))
        {
            return;
        }

        if (isCheck)
        {
            for (T tmp : items)
            {
                if (!this.allItems.contains(tmp))
                {
                    this.cmbModel.addElement(tmp);
                }
            }
        }
        else
        {
            this.cmbModel.addElementList(items);
        }

        if (this.isInitialed == false)
        {
            this.updateComboBox();
        }
    }

    public void addItems(final T[] items)
    {
        this.addItems(items, false);
    }

    public void addItems(final T[] items, final boolean isCheck)
    {
        if ((items == null) || (items.length == 0))
        {
            return;
        }

        if (isCheck)
        {
            for (T tmp : items)
            {
                if (!this.allItems.contains(tmp))
                {
                    this.cmbModel.addElement(tmp);
                }
            }
        }
        else
        {
            for (Object tmp : items)
            {
                this.cmbModel.addElement(tmp);
            }
        }

        if (this.isInitialed == false)
        {
            this.updateComboBox();
        }
    }

    private void changeSelect(final Object item)
    {
        if (this.changed)
        {
            this.cmbEditor.setItem(item);
            this.cmbModel.setSelectedItem(item);
            this.oldItem = item;
            this.changed = false;
        }
    }

    @Override
    public void fireActionEvent()
    {
        if (this.freezeFilter)
        {
            super.fireActionEvent();
        }
    }

    private void fireActionPerformed()
    {
        super.fireActionEvent();
    }

    private void fixMaxVisibleItemCount()
    {
        if (this.filteredItems.size() < FilterableComboBox.MAXROWCOUNT)
        {
            this.setMaximumRowCount(this.filteredItems.size());
        }
        else
        {
            this.setMaximumRowCount(FilterableComboBox.MAXROWCOUNT);
        }
    }

    /**
     * 根据字符串查找出一个Item.<br/>
     *
     * @param filterStr
     * @return T
     */
    public T getFilterItem(final String filterStr)
    {
        Filter tmpFilter = new StartsWithFilter(filterStr);
        for (T item : this.allItems)
        {
            if (tmpFilter.accept(item))
            {
                return item;
            }
        }

        return null;
    }

    @Override
    public T getSelectedItem()
    {
        return this.cmbModel.getSelectedItem();
    }

    protected void handleChange()
    {
        if (this.freezeFilter)
        {
            return;
        }
        if (this.setting)
        {
            return;
        }
        this.filtering = true;

        Filter filter = null;
        if (this.text.getText().length() > 0)
        {
            this.orginStr = this.text.getText();
            filter = new StartsWithFilter(this.orginStr);
        }
        else
        {
            this.orginStr = null;
        }

        this.setFilter(filter);
        // A bit nasty but it seems to get the popup validated properly
        // setPopupVisible(false);

        if (this.filteredItems.size() > 0)
        {
            this.setPopupVisible(true);
        }
        else
        {
            this.setPopupVisible(false);
        }

        this.filtering = false;
    }

    private void init()
    {
        this.setModel(this.cmbModel);
        this.cmbEditor = new FilterableEditor();
        this.setEditor(this.cmbEditor);
        this.setRenderer(this.listCellRenderer);
        this.addKeyListener(this.keyListener);
    }

    public boolean isInitialed()
    {
        return this.isInitialed;
    }

    private void reloadComboBox()
    {
        this.setting = true;
        if (this.filteredItems.size() < this.allItems.size())
        {
            this.filteredItems.clear();
            this.filteredItems.addAll(this.allItems);
            this.updateComboBox();
        }
        this.setting = false;
    }

    @Override
    public void removeAllItems()
    {
        super.removeAllItems();
        this.filteredItems.clear();
        this.allItems.clear();
    }

    @Override
    public void removeItem(final Object item)
    {
        this.allItems.remove(item);
        this.cmbModel.removeElement(item);
        if (this.isInitialed == false)
        {
            this.fixMaxVisibleItemCount();
            this.cmbModel.fireIntervalRemoved(this, 0, this.cmbModel.getSize() - 1);
        }
    }

    @Override
    public void removeItemAt(final int index)
    {
        if (index < 0)
        {
            return;
        }
        if (index >= this.cmbModel.getSize())
        {
            return;
        }

        Object item = this.cmbModel.getElementAt(index);
        this.removeItem(item);
    }

    @Override
    protected void selectedItemChanged()
    {
        if (this.freezeFilter || (this.filtering == false))
        {
            super.selectedItemChanged();
        }
    }

    public void setDefaultItem(final T defaultItem)
    {
        this.defaultItem = defaultItem;
    }

    public void setAllowedMismatch(final boolean allowedMismatch)
    {
        this.allowedMismatch = allowedMismatch;
    }

    private void setFilter(final Filter filter)
    {
        this.filter = filter;
        this.cmbModel.updateFilteredItems();
    }

    public void setIsInitialed(final boolean isInitialed)
    {
        this.isInitialed = isInitialed;
        if (this.isInitialed)
        {
            this.updateComboBox();
        }
    }

    private void updateComboBox()
    {
        this.fixMaxVisibleItemCount();
        this.cmbModel.fireIntervalAdded(this, 0, this.filteredItems.size());
    }

}

接下来举例详细说明怎么IP控件和下拉框结合起来放进表格。

发布了36 篇原创文章 · 获赞 5 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/ccren/article/details/38515299