JCombox是JSwing中比较常用的组件,它显示的是一个项列表,扩展的是ListModil模型,利用ListCellBenderer来绘制下拉的每个Item。 同时Jcombox也可以作为一个容器, 将JLable, JCheckbox等做为Item显示在下拉项中,构成一个复合组件。下面以下拉选项中是JCheckBox选项(如下图所示)来分析如何构建JCombox的组合组件。
首先,JCombox是以ListCellBenderer接口来绘制下拉Item的,所以,Item由以前的String改成JComponent, 必须重新实现该接口,制作自己的Renderer。 ListCellRenderer接口就一个方法:getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {},
其中,各个参数的定义如下:
list: 正在绘制的JList, 比如下拉菜单
value: 选中的Item的值,可以是string, 也可以是自己写的一个Item,用list.getModel().getElementAt(index) 返回的值;
index: 选中的Item的索引,-1代表什么都不选择;
isSelected: 该单元格/Item是否被选中;
cellHasFocus: 该Item是否拥有焦点;
ListCellRenderer的工作原理: 在绘制JList的每个Cell之前,它会去调用getListCellRendererComponent( ),得到一个Component,并
将这个Component绘制在正确的位置.因为getListCellRendererComponent( )返回的是Component,所以我们几乎可以扩展任意一个Component,来改变JList,JComboBox等的外观(关于渲染器或者绘制器的原理和知识见如下文章:http://piscesky.iteye.com/blog/281996 http://www.java3z.com/cwbwebhome/article/pr/sw13.jsp)。
将这个Component绘制在正确的位置.因为getListCellRendererComponent( )返回的是Component,所以我们几乎可以扩展任意一个Component,来改变JList,JComboBox等的外观(关于渲染器或者绘制器的原理和知识见如下文章:http://piscesky.iteye.com/blog/281996 http://www.java3z.com/cwbwebhome/article/pr/sw13.jsp)。
实现自己的Renderer,需要extends你要放在下拉菜单中的JComponent, 并且implements ListCellRenderer接口,然后return自己。代码如下:
1 public class ComboCheckBoxRenderer extends JCheckBox implements ListCellRenderer, Serializable 2 { 3 private DefaultListCellRenderer labelRenderer; 4 5 public ComboCheckBoxRenderer() 6 { 7 super(); 8 setOpaque(true); 9 setBorder(noFocusBorder); 10 } 11 12 public Component getListCellRendererComponent( 13 JList list, 14 Object value, 15 int index, 16 boolean isSelected, 17 boolean cellHasFocus) 18 { 19 ComboCheckBoxEntry item = (ComboCheckBoxEntry) value; 20 if (item.key == KEY_TITLE) 21 { 22 if (labelRenderer == null) 23 labelRenderer = new DefaultListCellRenderer(); 24 return labelRenderer.getListCellRendererComponent(list, item.value, index, isSelected, cellHasFocus); 25 } 26 27 setComponentOrientation(list.getComponentOrientation()); 28 if (isSelected) 29 { 30 setBackground(list.getSelectionBackground()); 31 setForeground(list.getSelectionForeground()); 32 } 33 else 34 { 35 setBackground(list.getBackground()); 36 setForeground(list.getForeground()); 37 } 38 39 setSelected(item.checked); 40 setText(item.value); 41 setEnabled(list.isEnabled()); 42 if (isAllItemSelected() && item.key != KEY_ALL) 43 setEnabled(false); 44 setFont(list.getFont()); 45 setBorder((cellHasFocus) ? UIManager.getBorder("List.focusCellHighlightBorder") : noFocusBorder); 46 47 return this; 48 } 49 }
也可以加上提示为每个选项,比如39行改为如下,就可以实现当选择某个item时候,提示你已经选择该item。
if(item.checked){ list.setToolTipText(item.value + "have been selected"); } setSelected(item.checked);
其次, 定制符合特定的Item结构来满足你的下拉菜单的显示,代码如下:
public class ComboCheckBoxEntry { boolean checked; Object key; String value; public ComboCheckBoxEntry(boolean checked, Object key, String value) { this.checked = checked; this.key = key; this.value = value; } public String toString() { return value == null ? "" : value; } }
定制的Item结构也可以继承一些接口,来实现一些功能,比如实现SelectEnable接口,使得Item具有是否采用线的样式,其实不继承这个接口,直接在Item里面增加一个属性isLineEnabled也行:
public interface LineEnable { public void setLineEnabled(boolean isLineEnabled); public boolean isLineEnabled(); } public class MyComboBoxItem implements SelectEnable, LineEnable { /*对于特殊的JCombobox设置Item时,设置MyComboBoxItem就可以使Item具有选择可否和是否是线的样式.*/ private Object comboxItem = null; boolean isLineEnabled = false; }
最后,设置JCombox的UI来控制显示,代码如下:
public class ComboCheckBoxUI extends MetalComboBoxUI { protected ComboPopup createPopup() { ComboCheckPopUp comboCheckPopUp = new ComboCheckPopUp(comboBox); list = comboCheckPopUp.getList(); return comboCheckPopUp; } public class ComboCheckPopUp extends BasicComboPopup { public ComboCheckPopUp(JComboBox cBox) { super(cBox); } @Override protected JList createList() { final ComboBoxModel model = comboBox.getModel(); // wrapper model 屏蔽Title Item Entry final DefaultComboBoxModel wrapper = new DefaultComboBoxModel(); model.addListDataListener(new ListDataListener() { public void contentsChanged(ListDataEvent e) {} public void intervalAdded(ListDataEvent e) { ComboCheckBoxEntry item = (ComboCheckBoxEntry) model.getElementAt(e.getIndex0()); if (item.key != KEY_TITLE) wrapper.addElement(item); } public void intervalRemoved(ListDataEvent e) { int index = e.getIndex0() - 1; if (index >= 0 && index <= wrapper.getSize()) wrapper.removeElementAt(index); } }); return new JList(wrapper) { public void processMouseEvent(MouseEvent e) { if (e.isControlDown()) { e = new MouseEvent((Component) e.getSource(), e.getID(), e.getWhen(), e.getModifiers() ^ InputEvent.CTRL_MASK, e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger()); } super.processMouseEvent(e); } }; } @Override protected int getPopupHeightForRowCount(int maxRowCount) { int minRowCount = Math.min(maxRowCount, list.getModel().getSize()); int height = 0; ListCellRenderer renderer = list.getCellRenderer(); Object value = null; for (int i = 0; i < minRowCount; ++i) { value = list.getModel().getElementAt(i); Component c = renderer.getListCellRendererComponent(list, value, i, false, false); height += c.getPreferredSize().height; } return height == 0 ? 100 : height; } @Override protected MouseListener createListMouseListener() { return new CheckBoxListMouseHandler(); } protected class CheckBoxListMouseHandler extends MouseAdapter { @Override public void mousePressed(MouseEvent anEvent) { int index = list.getSelectedIndex(); ComboCheckBoxEntry item = (ComboCheckBoxEntry) list.getModel().getElementAt(index); if (item == null) return; if (item.key == KEY_TITLE) return; if (isAllItemSelected() && item.key != KEY_ALL) return; if (item.key == KEY_ALL && item.checked == false) { ListModel mdl = list.getModel(); for (int i = 0; i < mdl.getSize(); i++) { ComboCheckBoxEntry entry = (ComboCheckBoxEntry) mdl.getElementAt(i); if (entry.key == KEY_ALL || entry.key == KEY_TITLE) continue; entry.checked = false; } } boolean checked = !item.checked; int size = list.getModel().getSize(); item.checked = checked; updateListBoxSelectionForEvent(anEvent, false); fireItemStateChanged(new ItemEvent(MultiSelectionComboBox.this, 0, null, 0)); Rectangle rect = list.getCellBounds(0, size - 1); list.repaint(rect); } } } }
现在测试如下:
1 public class TestJcombox { 2 3 /** 4 * @param args 5 */ 6 public static void main(String[] args) { 7 // TODO Auto-generated method stub 8 9 JDialog jDialog = new JDialog(); 10 jDialog.setLayout(new FlowLayout()); 11 MultiSelectionComboBox multiSel = new MultiSelectionComboBox("", true); 12 multiSel.addItem(1, "中国"); 13 multiSel.addItem(2, "印度"); 14 multiSel.addItem(3, "日本"); 15 jDialog.add(multiSel); 16 jDialog.setSize(200, 300); 17 jDialog.setVisible(true); 18 } 19 20 }
就可以得到开篇图所说的效果。
引文:
http://www.cnblogs.com/zhangxz/archive/2012/10/11/2720241.html
http://www.blogjava.net/zeyuphoenix/archive/2010/04/12/318014.html
http://www.java3z.com/cwbwebhome/article/pr/sw13.jsp
http://piscesky.iteye.com/blog/281996