Swing:支持自动过滤的 JComboBox

根据输入内容过滤可选项

支持键盘和鼠标的选择

 

package component;

import java.awt.Component;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Vector;

import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
import javax.swing.event.ListDataEvent;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.plaf.metal.MetalComboBoxUI;

/**
 * 自动过滤下拉框
 * @author Sun
 *
 */
@SuppressWarnings("serial")
public class JFilterComboBox extends JComboBox {
	
	/**
	 * 显示用模型
	 */
	protected static DefaultComboBoxModel showModel = new DefaultComboBoxModel();
	/**
	 * 正在选择
	 */
	private boolean selectingItem;
	
	/**
	 * 创建一个 <code>JFilterComboBox</code>,
	 * 其项取自现有的 <code>ComboBoxModel</code>。
	 * 由于提供了 <code>ComboBoxModel</code>,
	 * 使用此构造方法创建的组合框不创建默认组合框模型,
	 * 这可能影响插入、移除和添加方法的行为方式。
	 * @param aModel - 提供显示的项列表的 <code>ComboBoxModel</code>
	 */
	public JFilterComboBox(ComboBoxModel aModel) {
		super(aModel);
		initialize();
	}

	/**
	 * 创建包含指定数组中的元素的 <code>JFilterComboBox</code>。
	 * 默认情况下,选择数组中的第一项(因而也选择了该项的数据模型)。 
	 * @param items - 要插入到组合框的对象数组
	 */
	public JFilterComboBox(final Object items[]) {
		super(items);
		initialize();
	}
	
	/**
	 * 创建包含指定 <code>Vector</code> 中的元素的 <code>JFilterComboBox</code>。
	 * 默认情况下,选择数组中的第一项(因而也选择了该项的数据模型)。 
	 * @param items - 要插入到组合框的向量数组
	 */
	public JFilterComboBox(Vector<?> items) {
		super(items);
		initialize();
	}
	
	/**
	 * 创建具有默认数据模型的 <code>JFilterComboBox</code>。
	 * 默认的数据模型为空对象列表。使用 <code>addItem</code> 添加项。
	 * 默认情况下,选择数据模型中的第一项。
	 */
	public JFilterComboBox() {
		super();
		initialize();
	}
	
	private void initialize() {
		showModel.addListDataListener(this);
	}
	
	@Override
	public void updateUI() {
		setUI(new MetalFilterComboBoxUI());
		ListCellRenderer renderer = getRenderer();
		if (renderer instanceof Component) {
			SwingUtilities.updateComponentTreeUI((Component) renderer);
		}
	}
	
	@Override
	public Object getSelectedItem() {
		return showModel.getSelectedItem();
	}

	@Override
	public void setSelectedItem(Object anObject) {
		Object oldSelection = selectedItemReminder;
		Object objectToSelect = anObject;
		if (oldSelection == null || !oldSelection.equals(anObject)) {

			if (anObject != null && !isEditable()) {
				boolean found = false;
				for (int i = 0; i < showModel.getSize(); i++) {
					Object element = showModel.getElementAt(i);
					if (anObject.equals(element)) {
						found = true;
						objectToSelect = element;
						break;
					}
				}
				if (!found) {
					return;
				}
			}

			selectingItem = true;
			showModel.setSelectedItem(objectToSelect);
			selectingItem = false;

			if (selectedItemReminder != showModel.getSelectedItem()) {
				selectedItemChanged();
			}
		}
		fireActionEvent();
	}

	@Override
	public void setSelectedIndex(int anIndex) {
		int size = showModel.getSize();
		if (anIndex == -1 || size == 0) {
			setSelectedItem(null);
		} else if (anIndex < -1) {
			throw new IllegalArgumentException("setSelectedIndex: " + anIndex
					+ " out of bounds");
		} else if (anIndex >= size) {
			setSelectedItem(showModel.getElementAt(size - 1));
		} else {
			setSelectedItem(showModel.getElementAt(anIndex));
		}
	}

	@Override
	public int getSelectedIndex() {
		Object sObject = showModel.getSelectedItem();
		int i, c;
		Object obj;

		for (i = 0, c = showModel.getSize(); i < c; i++) {
			obj = showModel.getElementAt(i);
			if (obj != null && obj.equals(sObject))
				return i;
		}
		return -1;
	}

	@Override
	public void contentsChanged(ListDataEvent e) {
		Object oldSelection = selectedItemReminder;
		Object newSelection = showModel.getSelectedItem();
		if (oldSelection == null || !oldSelection.equals(newSelection)) {
			selectedItemChanged();
			if (!selectingItem) {
				fireActionEvent();
			}
		}
	}

	@Override
	protected void selectedItemChanged() {
		if (selectedItemReminder != null) {
			fireItemStateChanged(new ItemEvent(this,
					ItemEvent.ITEM_STATE_CHANGED, selectedItemReminder,
					ItemEvent.DESELECTED));
		}

		selectedItemReminder = showModel.getSelectedItem();

		if (selectedItemReminder != null) {
			fireItemStateChanged(new ItemEvent(this,
					ItemEvent.ITEM_STATE_CHANGED, selectedItemReminder,
					ItemEvent.SELECTED));
		}
	}

	@Override
	public void intervalAdded(ListDataEvent e) {
		if (selectedItemReminder != showModel.getSelectedItem()) {
			selectedItemChanged();
		}
	}

	@Override
	public void setEditable(boolean aFlag) {
		super.setEditable(true);
	}

	/**
	 * 返回显示用模型
	 * @return
	 */
	public DefaultComboBoxModel getShowModel() {
		return showModel;
	}
	
	/**
	 * Metal L&F 风格的 UI 类
	 * @author Sun
	 *
	 */
	class MetalFilterComboBoxUI extends MetalComboBoxUI {
		
		/**
		 * 编辑区事件监听器
		 */
		protected EditorListener editorListener;
		/**
		 * 该 UI 类负责绘制的控件
		 */
		protected JFilterComboBox filterComboBox;

		@Override
		public void installUI(JComponent c) {
			filterComboBox = (JFilterComboBox) c;
			filterComboBox.setEditable(true);
			super.installUI(c);
		}

		@Override
		public void configureEditor() {
			super.configureEditor();
			editor.addKeyListener(getEditorListener());
			editor.addMouseListener(getEditorListener());
			editor.addFocusListener(getEditorListener());
		}
		
		@Override
		public void unconfigureEditor() {
			super.unconfigureEditor();
			if (editorListener != null) {
				editor.removeKeyListener(editorListener);
				editor.removeMouseListener(editorListener);
				editor.removeFocusListener(editorListener);
				editorListener = null;
			}
		}
		
		@Override
		protected ComboPopup createPopup() {
			return new FilterComboPopup(filterComboBox);
		}
		
		/**
		 * 初始化并返回编辑区事件监听器
		 * @return
		 */
		protected EditorListener getEditorListener() {
			if (editorListener == null) {
				editorListener = new EditorListener();
			}
			return editorListener;
		}

		/**
		 * 按关键字进行查询,该方法中,可以自行加入各种查询算法
		 */
		protected void findMatchs() {
			ComboBoxModel model = filterComboBox.getModel();
			DefaultComboBoxModel showModel = filterComboBox.getShowModel();
			showModel.removeAllElements();
			for (int i = 0; i < model.getSize(); i++) {
				String name = model.getElementAt(i).toString();
				if (name.indexOf(getEditorText()) >= 0) {
					showModel.addElement(model.getElementAt(i));
				}
			}
			((FilterComboPopup)popup ).repaint();
		}
		
		/**
		 * 返回编辑区文本
		 * @return
		 */
		private String getEditorText() {
			return filterComboBox.getEditor().getItem().toString();
		}
		
		/**
		 * 弹出面板类
		 * @author Sun
		 *
		 */
		class FilterComboPopup extends BasicComboPopup {

			public FilterComboPopup(JComboBox combo) {
				super(combo);
			}
			
			@Override
			protected JList createList() {
				JList list = super.createList();
				list.setModel(filterComboBox.getShowModel());
				return list;
			}
			
			@Override
			public void setVisible(boolean b) {
				super.setVisible(b);
				if (!b) {
					comboBox.getEditor().setItem(comboBox.getSelectedItem());
				}
			}
			
			@Override
			public void show() {
				findMatchs();
				super.show();
			}

		}
		
		/**
		 * 编辑区事件监听器类
		 * @author Sun
		 *
		 */
		class EditorListener implements KeyListener,MouseListener, FocusListener {
			
			/**
			 * 旧文本,用于键盘输入时的比对
			 */
			private String oldText = "";
			
			@Override
			public void keyReleased(KeyEvent e) {
				String newText = getEditorText();
				if (!newText.equals(oldText)) {
					findMatchs();
				}
			}
			
			@Override
			public void keyPressed(KeyEvent e) {
				oldText = getEditorText();
				if (!isPopupVisible(filterComboBox)) {
					setPopupVisible(filterComboBox, true);
				}
			}

			@Override
			public void keyTyped(KeyEvent e) {
				findMatchs();
			}

			@Override
			public void mouseClicked(MouseEvent e) {}

			@Override
			public void mousePressed(MouseEvent e) {
				if (!isPopupVisible(filterComboBox)) {
					setPopupVisible(filterComboBox, true);
				}
			}

			@Override
			public void mouseReleased(MouseEvent e) {}

			@Override
			public void mouseEntered(MouseEvent e) {}

			@Override
			public void mouseExited(MouseEvent e) {}
			
			@Override
			public void focusGained(FocusEvent e) {
				if (!isPopupVisible(filterComboBox)) {
					setPopupVisible(filterComboBox, true);
				}
			}
			
			@Override
			public void focusLost(FocusEvent e) {}

		}

	}
	
	/**
	 * 使用示例
	 * @param args
	 */
	public static void main(String... args) {
		Vector<String> data = new Vector<String>(0);
		data.add("ab");
		data.add("ac");
		data.add("bb");
		data.add("cc");
		JPanel panel = new JPanel();
		panel.add(new JFilterComboBox(data));
		JFrame frame = new JFrame();
		frame.setSize(400, 300);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setContentPane(panel);
		frame.setVisible(true);
	}

}


 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值