转自 一文online
接着昨天的MyJTextField,我们继续为JTextField增强,今天我们为MyJTextField增加一个泡泡提示。先看图片:
当输入第三个字符'a'时,由于昨天我们的MyJTextField做了处理,所以'a'不能被输入,而且弹出泡泡提示你下次不要了哟!
同样的,下一张图片:
为MyJTextField增加泡泡提示功能要做以下几件事情:
1. 泡泡窗口
2. 显示的时候计算位置
这里我使用了Bernhard Pauler的BalloonTip包的BalloonBorder代码,改造了BalloonTip代码,好废话不说,放上完整代码后再解释:
1
/**
2
* @(#)MyJTextField.java 0.1.1 2007-9-8
3
*/
4
package
ruislan;
5
6
import
java.awt.BorderLayout;
7
import
java.awt.Color;
8
import
java.awt.Component;
9
import
java.awt.Container;
10
import
java.awt.Font;
11
import
java.awt.Point;
12
import
java.awt.event.ComponentAdapter;
13
import
java.awt.event.ComponentEvent;
14
import
java.awt.event.KeyAdapter;
15
import
java.awt.event.KeyEvent;
16
17
import
javax.swing.Icon;
18
import
javax.swing.ImageIcon;
19
import
javax.swing.JDialog;
20
import
javax.swing.JFrame;
21
import
javax.swing.JLabel;
22
import
javax.swing.JLayeredPane;
23
import
javax.swing.JPanel;
24
import
javax.swing.JTextField;
25
import
javax.swing.border.EmptyBorder;
26
27
import
net.java.balloontip.BalloonBorder;
28
29
30
/**
31
* Custom JTextField.
32
*
33
*
@version
0.1.1, 2007-9-9
34
*
@author
ruislan <a href="mailto:[email protected]"/>
35
*/
36
public
class
MyJTextField
extends
JTextField {
37
private
static
final
Color TIP_COLOR
=
new
Color(
255
,
255
,
225
);
38
private
int
limit
=
Integer.MAX_VALUE;
39
private
boolean
numberOnly;
40
private
CoolToolTip numberTip;
41
private
CoolToolTip limitTip;
42
private
ImageIcon tipIcon;
43
44
public
MyJTextField() {
45
initComponents();
46
initEventListeners();
47
}
48
49
private
void
initComponents() {
50
tipIcon
=
new
ImageIcon(MyJTextField.
class
.getResource(
"
tip.gif
"
));
51
52
numberTip
=
new
CoolToolTip(
this
, TIP_COLOR, getColumns(),
10
);
53
numberTip.setText(
"
只能输入数字!
"
);
54
numberTip.setIcon(tipIcon);
55
numberTip.setIconTextGap(
10
);
56
57
limitTip
=
new
CoolToolTip(
this
, TIP_COLOR, getColumns(),
10
);
58
limitTip.setIcon(tipIcon);
59
limitTip.setIconTextGap(
10
);
60
}
61
62
private
void
initEventListeners() {
63
addKeyListener(
new
KeyAdapter() {
64
@Override
65
public
void
keyTyped(KeyEvent e) {
66
if
(getText().length()
+
1
>
limit) {
67
deleteInputChar(e);
68
limitTip.setVisible(
true
);
69
return
;
70
}
else
{
71
limitTip.setVisible(
false
);
72
}
73
if
(numberOnly) {
74
char
input
=
e.getKeyChar();
75
if
(
!
Character.isDigit(input)) {
76
numberTip.setVisible(
true
);
77
deleteInputChar(e);
78
}
else
{
79
numberTip.setVisible(
false
);
80
}
81
}
82
}
83
84
private
void
deleteInputChar(KeyEvent source) {
85
source.setKeyChar((
char
) KeyEvent.VK_CLEAR);
86
}
87
});
88
}
89
90
public
void
setMaxTextLength(
int
limit) {
91
if
(limit
<
0
) {
92
return
;
93
}
94
this
.limit
=
limit;
95
limitTip.setText(String.format(
"
超过最大长度 /
"
%
d/
""
, limit));
96
}
97
98
public
int
getMaxTextLength() {
99
return
limit;
100
}
101
102
public
void
setNumberOnly(
boolean
numberOnly) {
103
this
.numberOnly
=
numberOnly;
104
}
105
106
public
boolean
isNumberOnly() {
107
return
this
.numberOnly;
108
}
109
110
private
class
CoolToolTip
extends
JPanel {
111
private
JLabel label
=
new
JLabel();
112
private
boolean
haveShowPlace;
113
114
private
Component attachedComponent;
115
116
public
CoolToolTip(Component attachedComponent, Color fillColor,
117
int
borderWidth,
int
offset) {
118
this
.attachedComponent
=
attachedComponent;
119
120
label.setBorder(
new
EmptyBorder(borderWidth, borderWidth,
121
borderWidth, borderWidth));
122
label.setBackground(fillColor);
123
label.setOpaque(
true
);
124
label.setFont(
new
Font(
"
system
"
,
0
,
12
));
125
126
setOpaque(
false
);
127
this
.setBorder(
new
BalloonBorder(fillColor, offset));
128
this
.setLayout(
new
BorderLayout());
129
add(label);
130
131
setVisible(
false
);
132
133
//
if the attached component is moved while the balloon tip is
134
//
visible, we need to move as well
135
attachedComponent.addComponentListener(
new
ComponentAdapter() {
136
public
void
componentMoved(ComponentEvent e) {
137
if
(isShowing()) {
138
determineAndSetLocation();
139
}
140
}
141
});
142
143
}
144
145
private
void
determineAndSetLocation() {
146
Point location
=
attachedComponent.getLocation();
147
setBounds(location.x, location.y
-
getPreferredSize().height,
148
getPreferredSize().width, getPreferredSize().height);
149
}
150
151
public
void
setText(String text) {
152
label.setText(text);
153
}
154
155
public
void
setIcon(Icon icon) {
156
label.setIcon(icon);
157
}
158
159
public
void
setIconTextGap(
int
iconTextGap) {
160
label.setIconTextGap(iconTextGap);
161
}
162
163
public
void
setVisible(
boolean
show) {
164
if
(show) {
165
determineAndSetLocation();
166
findShowPlace();
167
}
168
super
.setVisible(show);
169
}
170
171
private
void
findShowPlace() {
172
if
(haveShowPlace) {
173
return
;
174
}
175
//
we use the popup layer of the top level container (frame or
176
//
dialog) to show the balloon tip
177
//
first we need to determine the top level container
178
Container parent
=
attachedComponent.getParent();
179
JLayeredPane layeredPane;
180
while
(
true
) {
181
if
(parent
instanceof
JFrame) {
182
layeredPane
=
((JFrame) parent).getLayeredPane();
183
break
;
184
}
else
if
(parent
instanceof
JDialog) {
185
layeredPane
=
((JDialog) parent).getLayeredPane();
186
break
;
187
}
188
parent
=
parent.getParent();
189
}
190
layeredPane.add(
this
, JLayeredPane.POPUP_LAYER);
191
haveShowPlace
=
true
;
192
}
193
}
194
}
195
测试代码:
1
package
ruislan.examples;
2
3
import
java.awt.Container;
4
import
java.awt.Dimension;
5
import
java.awt.GridBagLayout;
6
import
java.awt.Toolkit;
7
8
import
javax.swing.JFrame;
9
10
import
ruislan.MyJTextField;
11
12
public
class
MyJTextFieldTest {
13
14
public
static
void
main(String[] args) {
15
JFrame frame
=
new
JFrame();
16
Container contentPane
=
frame.getContentPane();
17
contentPane.setLayout(
new
GridBagLayout());
18
final
MyJTextField textField
=
new
MyJTextField();
19
textField.setNumberOnly(
true
);
20
textField.setColumns(
15
);
21
textField.setMaxTextLength(
5
);
22
contentPane.add(textField);
23
24
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
25
frame.setSize(
320
,
240
);
26
centerWindow(frame);
27
frame.setTitle(
"
MyJTextField
"
);
28
frame.setVisible(
true
);
29
}
30
31
public
static
void
centerWindow(Container window) {
32
//
get the size of the screen
33
Dimension dim
=
Toolkit.getDefaultToolkit().getScreenSize();
34
35
//
determine the new location of the window
36
int
w
=
window.getSize().width;
37
int
h
=
window.getSize().height;
38
int
x
=
(dim.width
-
w)
/
2
;
39
int
y
=
(dim.height
-
h)
/
2
;
40
41
//
move the window
42
window.setLocation(x, y);
43
}
44
}
45
就以上代码解释一下
1. 为什么CoolToolTip不使用JWindow而使用JPanel?
不错,JWindow可以放置在屏幕的任意位置,而且也是我的首选,但是JWindow是调用native代码来绘图,没有提供可以改变形状的API(我 们需要泡泡嘛),所以使用了JPanel,那么这就具有了局限性,第一个就是我们在显示泡泡的时候必须找到最高级的组件来显示,以便覆盖到其下的所有子组 件,第二个就是显示的区域受到了最高级组件的局限,例如JFrame上只有一个MyJTextField,而JFrame.pack了之后大小刚好是 MyJTextField的大小,这样就看不到CoolToolTip了。
2. 为什么CoolToolTip在setVisible的时候才找一个要show的地方?
这个操作在Bernhard Pauler的BalloonTip代码中是由构造方法来完成的,但是这里就会出现一个问题,对,NullPointer,因为Swing不像SWT,是 在component.add(child)方法调用的时候才设置child的Parent,而非在构造child的时候将component放入,所以 我们只有懒加载来避免这个情况发生。说到懒加载我发表一下一个题外意见,在下认为懒加载在swing中不要用得太过频繁,用户体验是第一位的,在下宁愿在 启动程序的时候看进度条也不愿在启动某个视图的时候突然卡死一小会。
3. 还有什么没有做的?
例如当输入Esc、Backspace、Enter等等按键的时候CoolToolTip也会跳出来show一把,我们必须过滤掉它们!
今天就到这里吧,下一次我们让MyJTextField更酷一点,我们为它加上Beep声音,当我们输入了非法字符的时候就beep、beep的叫唤,没有声音也没有泡泡提示的话有些人很可能是以为自己的键盘坏掉了。
本人就对QQ的登录窗口很有意见,每次我开机都是Eclipse和QQ一起打开的,一般操作都是先登录QQ,但是有一天我QQ后打开的,我正在happy coding,突然输入的字符全乱了套,我以为是输入的问题,结果是英文也,然后我认为可能是eclipse的问题(因为有的时候eclipse的代码编 辑区就无端不让操作了),换成了记事本,结果也是这个情况,然后就有点慌以为键盘坏掉了,毕竟换一个笔记本键盘很贵的说(在找问题其间多次路过QQ的登录 窗口,我还嫌其碍事最小化了),维修部电话打过之后他们叫我拿过去,我就准备关电脑,也不知道怎的,在我关闭了一系列的程序包括QQ登录窗口之后,我突然 又想最后确定一下,当然结果很明显又好了,这个时候我就像修复程序BUG一样,找到恢复正常前的最后一个动作,重复操作直到又不正常为止,终于发现是QQ 登录框的问题,气煞我也,差点就花钱换一个键盘了...