Posted on 2010-08-23 13:56
TWaver 閱讀(2036)
評(píng)論(1) 編輯 收藏
記得Delphi里面有一個(gè)TCheckListBox控件,是一個(gè)可打勾的列表。但是這個(gè)東西在Swing里面并沒有現(xiàn)成的。如今,我們就一起動(dòng)手制作一個(gè)。根據(jù)Java的管理,就叫JCheckListBox吧。
寫代碼之前,先考慮以下問題:
- 繼承:當(dāng)然是從Swing的JList繼承。
- 數(shù)據(jù)擴(kuò)充:對(duì)于JList來說,它是顯示了一系列Object。無論其類型如何,都用一個(gè)默認(rèn)的渲染器(DefaultListCellRenderer,從JLabel繼承而來)來畫,每個(gè)條目的文字用Object.toString()來設(shè)置。但是對(duì)于JCheckListBox來說,除了顯示文本外,還要考慮每個(gè)條目是否被選中,如果選中,要顯示“打勾”。所以,JList需要維護(hù)“每一個(gè)條目是否選中”的狀態(tài)信息。我們放在一個(gè)boolean數(shù)組中。
- 渲染器:默認(rèn)的Renderer肯定是不行了,無法顯示打勾。自然想到用JCheckBox來重新做一個(gè)渲染器,設(shè)置到JCheckListBox中。
- 鼠標(biāo)監(jiān)聽器:現(xiàn)在可以畫每個(gè)條目了,但還不夠,必須能響應(yīng)鼠標(biāo)的點(diǎn)擊以便Check/UnCheck才行。所以要在JCheckListBox上加一個(gè)鼠標(biāo)監(jiān)聽器來響應(yīng)鼠標(biāo)事件。當(dāng)然,如果你想讓它相應(yīng)鍵盤輸入(例如Ctrl+A全選)也可如法炮制。
- CheckListBoxModel:為了操作方便,這里還從AbstractListModel擴(kuò)充一個(gè)CheckListBoxModel,它能在條目Check變化時(shí)發(fā)送事件。
好了,由于代碼和原理都比較簡單,不再贅述,直接給出代碼,以及簡單注釋。
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
//這個(gè)boolean數(shù)組裝載所有item是否被check的信息。
8
9
private boolean[] checkedItems = null;
10
11
/** *//**
12
* 定義一個(gè)簡單的ListModel,它可以發(fā)送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
* 這里就覆蓋了一個(gè)構(gòu)造函數(shù)。其他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數(shù)組、安裝一個(gè)渲染器、安裝一個(gè)鼠標(biāo)監(jiān)聽器。
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
//這點(diǎn)代碼基本上從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
//雖然抄襲,可這里別忘了設(shè)置check信息。
86
this.setSelected(isChecked(index));
87
return this;
88
}
89
}
90
91
this.setCellRenderer(new MyCellRenderer());
92
//定義一個(gè)鼠標(biāo)監(jiān)聽器。如果點(diǎn)擊某個(gè)item,翻轉(zhuǎn)其check狀態(tài)。
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
* 翻轉(zhuǎn)指定item的check狀態(tài)。
107
* @param index int
108
*/
109
public void invertChecked(int index)
{
110
checkedItems[index] = !checkedItems[index];
111
//別忘了發(fā)送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個(gè)數(shù)
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索引的數(shù)組。
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中文社區(qū)之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("當(dāng)前沒有選擇。");
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("當(dāng)前沒有選擇。");
184
} else
{
185
String text = "當(dāng)前選擇:";
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
}
運(yùn)行效果如下圖:
