記得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變化時發送事件。
好了,由于代碼和原理都比較簡單,不再贅述,直接給出代碼,以及簡單注釋。
1
import java.awt.*;
2
import java.awt.event.*;
3
import javax.swing.*;
4
import javax.swing.event.*;
5
6
public 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
}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

運行效果如下圖: