Posted on 2010-08-23 13:56
TWaver 阅读(2031)
评论(1) 编辑 收藏
记得Delphi里面有一个TCheckListBox控件,是一个可打勾的列表。但是这个东西在Swing里面并没有现成的。如今,我们就一起动手制作一个。根据Java的管理,就叫JCheckListBox吧。
写代码之前,先考虑以下问题:
- 继承:当然是从Swing的JList继承。
- 数据扩充:对于JList来说,它是显示了一系列Object。无论其类型如何,都用一个默认的渲染器(DefaultListCellRenderer,从JLabel继承而来)来画,每个条目的文字用Object.toString()来设置。但是对于JCheckListBox来说,除了显示文本外,还要考虑每个条目是否被选中,如果选中,要显示“打勾”。所以,JList需要维护“每一个条目是否选中”的状态信息。我们放在一个boolean数组中。
- 渲染器:默认的Renderer肯定是不行了,无法显示打勾。自然想到用JCheckBox来重新做一个渲染器,设置到JCheckListBox中。
- 鼠标监听器:现在可以画每个条目了,但还不够,必须能响应鼠标的点击以便Check/UnCheck才行。所以要在JCheckListBox上加一个鼠标监听器来响应鼠标事件。当然,如果你想让它相应键盘输入(例如Ctrl+A全选)也可如法炮制。
- CheckListBoxModel:为了操作方便,这里还从AbstractListModel扩充一个CheckListBoxModel,它能在条目Check变化时发送事件。
好了,由于代码和原理都比较简单,不再赘述,直接给出代码,以及简单注释。
1import java.awt.*;
2import java.awt.event.*;
3import javax.swing.*;
4import javax.swing.event.*;
5
6public class JCheckListBox extends JList {
7 //这个boolean数组装载所有item是否被check的信息。
8
9 private boolean[] checkedItems = null;
10
11 /** *//**
12 * 定义一个简单的ListModel,它可以发送check变化事件。
13 */
14 class CheckListBoxModel extends AbstractListModel {
15
16 private Object[] items = null;
17
18 CheckListBoxModel(Object[] items) {
19 this.items = items;
20 }
21
22 public int getSize() {
23 return items.length;
24 }
25
26 public Object getElementAt(int i) {
27 return items[i];
28 }
29
30 protected void fireCheckChanged(Object source, int index) {
31 fireContentsChanged(source, index, index);
32 }
33
34 public Object getItem(int index) {
35 return items[index];
36 }
37 }
38
39 /** *//**
40 * 这里就覆盖了一个构造函数。其他JList你自己覆盖吧,反正super一下再init就OK了。
41 * @param items Object[]
42 */
43 public JCheckListBox(Object[] items) {
44 setModel(new CheckListBoxModel(items));
45 init();
46 }
47
48 /** *//**
49 * 初始化控件。包括初始化boolean数组、安装一个渲染器、安装一个鼠标监听器。
50 */
51 protected void init() {
52 checkedItems = new boolean[this.getModel().getSize()];
53 class MyCellRenderer extends JCheckBox implements ListCellRenderer {
54
55 public MyCellRenderer() {
56 setOpaque(true);
57 }
58
59 public Component getListCellRendererComponent(
60 JList list,
61 Object value,
62 int index,
63 boolean isSelected,
64 boolean cellHasFocus) {
65 //这点代码基本上从DefaultListCellRenderer.java中抄袭的。
66 setComponentOrientation(list.getComponentOrientation());
67 if (isSelected) {
68 setBackground(list.getSelectionBackground());
69 setForeground(list.getSelectionForeground());
70 } else {
71 setBackground(list.getBackground());
72 setForeground(list.getForeground());
73 }
74
75 if (value instanceof Icon) {
76 setIcon((Icon) value);
77 setText("");
78 } else {
79 setIcon(null);
80 setText((value == null) ? "" : value.toString());
81 }
82 setEnabled(list.isEnabled());
83 setFont(list.getFont());
84
85 //虽然抄袭,可这里别忘了设置check信息。
86 this.setSelected(isChecked(index));
87 return this;
88 }
89 }
90
91 this.setCellRenderer(new MyCellRenderer());
92 //定义一个鼠标监听器。如果点击某个item,翻转其check状态。
93 class CheckBoxListener extends MouseAdapter {
94
95 @Override
96 public void mouseClicked(MouseEvent e) {
97 int index = locationToIndex(e.getPoint());
98 invertChecked(index);
99 }
100 }
101
102 this.addMouseListener(new CheckBoxListener());
103 }
104
105 /** *//**
106 * 翻转指定item的check状态。
107 * @param index int
108 */
109 public void invertChecked(int index) {
110 checkedItems[index] = !checkedItems[index];
111 //别忘了发送event。
112 CheckListBoxModel model = (CheckListBoxModel) getModel();
113 model.fireCheckChanged(this, index);
114 this.repaint();
115 }
116
117 /** *//**
118 * 是否指定item被check。
119 * @param index int
120 * @return boolean
121 */
122 public boolean isChecked(int index) {
123 return checkedItems[index];
124 }
125
126 /** *//**
127 * 获得选中的item个数
128 */
129 public int getCheckedCount() {
130 int result = 0;
131 for (int i = 0; i < checkedItems.length; i++) {
132 if (checkedItems[i]) {
133 result++;
134 }
135 }
136 return result;
137 }
138
139 /** *//**
140 * 所有选中item索引的数组。
141 */
142 public int[] getCheckedIndices() {
143 int[] result = new int[getCheckedCount()];
144 int index = 0;
145 for (int i = 0; i < checkedItems.length; i++) {
146 if (checkedItems[i]) {
147 result[index] = i;
148 index++;
149 }
150 }
151 return result;
152 }
153
154 public static void main(String[] args) throws Exception {
155
156 Font font = new Font("微软雅黑", Font.PLAIN, 12);
157
158 JFrame frame = new JFrame("TWaver中文社区之Swing探秘");
159
160 final JCheckListBox list = new JCheckListBox(new Object[]{"张三", "李四", "王二麻子", "木头六","小七子"});
161 list.setFont(font);
162 frame.getContentPane().add(new JScrollPane(list), BorderLayout.CENTER);
163 JButton button = new JButton("OK");
164 button.addActionListener(new ActionListener() {
165
166 public void actionPerformed(ActionEvent e) {
167 System.exit(0);
168 }
169 });
170 frame.getContentPane().add(button, BorderLayout.SOUTH);
171 final JLabel label = new JLabel("当前没有选择。");
172 label.setFont(font);
173 list.getModel().addListDataListener(new ListDataListener() {
174
175 public void intervalAdded(ListDataEvent e) {
176 }
177
178 public void intervalRemoved(ListDataEvent e) {
179 }
180
181 public void contentsChanged(ListDataEvent e) {
182 if (list.getCheckedCount() == 0) {
183 label.setText("当前没有选择。");
184 } else {
185 String text = "当前选择:";
186 int[] indices = list.getCheckedIndices();
187 for (int i = 0; i < indices.length; i++) {
188 text += ((CheckListBoxModel) list.getModel()).getItem(indices[i]).toString() + ",";
189 }
190 label.setText(text);
191 }
192 }
193 });
194 frame.getContentPane().add(label, BorderLayout.NORTH);
195 frame.setBounds(300, 300, 400, 200);
196 frame.setVisible(true);
197 }
198}
运行效果如下图: