在写一个模拟QQ的简易网络聊天程序,主要功能就是连到服务端的各个客户端都能实时收发到各客户端发的消息,只要有客户端发消息,那么所有客户端都将通过服务端这个中转站来接收到这个消息。
逻辑功能很简单,一会就写好了。但是在后续考虑用户使用方便时,需要通过回车“ENTER”按键来直接发送消息,而不是每次发消息后都要点击你的“Send”按钮,这对用户就太不友好了,所以自己的第一思路就是通过键盘监听来实现,即一旦用户按下回车键就在内部做出等同“send”按钮行为的响应。嘻嘻,自己当时以为很简单的,于是立即实施写出了下面的代码:
sendTextArea.addKeyListener(new SendMessage());
class SendMessage extends KeyAdapter implements ActionListener{
@Override
public void actionPerformed(ActionEvent e){
sendMessage();
}
@Override
public void keyPressed(KeyEvent e){
if(e.getKeyCode() == KeyEvent.VK_ENTER){
sendMessage();
}
}
public void sendMessage(){
if(sendTextArea.getText().length() != 0) {
clientWriter.println(userName + ":" + sendTextArea.getText());
sendTextArea.setText(null);
sendTextArea.requestFocus();
} else {
receiveTextArea.append("QQChat Server: You can't input for nothing, please input something again!\n");
}
}
}
执行后是下面的结果: 看,输入完hi之后发送消息的文本域没有被置为空而是移到了下一行的开始位置。。。当时很奇怪,再试了几次发现都一样:
自己期望的自然是光标能回到起始处,于是自己开始筛查原因,推测如下:造成这个现象和事件监听处理的模型有关。当给某个组件注册添加监听器如此例中为发送文本域sendTextArea添加键盘监听器keyListener时,只要鼠标焦点在sendTextArea中监听器就会随时监听你的键盘按键事件并立即做出相应响应,此时你按下”hi”没反应【很显然因为你比较的键值是enter键嘛】。在你回车时,监听器立马会对此事进行响应做出你内部定义的行为keyPressed,甚至在——文本域输出该按键应有的行为前做出响应。这句话很重要,就直接解释了为什么本应该重置为空“”的发送文本域又多了一行。正常来说程序是这样执行的:按下enter键——》执行enter键在文本域中原有的行为——即换行并将光标移到下一行的起始处;但是你加了键盘监听后,一旦你按键产生键盘事件,键盘监听器的行为要优于按键原有的行为,即程序先进入keyPressed方法执行完动作(发送信息文本并将发送文本域置空)再回去执行enter键的原有行为,然后正如你所看到的——在发送完消息后又多了一行。。。
所以综上,这个键盘监听的方法虽然可以让我们实现发送消息的功能操作,但是由于后续不理想的行为而放弃该方法。转而继续思考解决方案,就考虑莫不如直接给文本域注册个快捷键吧,然后通过搜索查找资料,得到inputMap, actionMap来给组件添加快捷键的方法(具体用法请参考官方API文档)!具体代码如下:
sendTextArea.getInputMap(JComponent.WHEN_FOCUSED).put(
KeyStroke.getKeyStroke('\n'),
"send");
sendTextArea.getActionMap().put("send", new SendMessage());
class SendMessage extends AbstractAction{
@Override
public void actionPerformed(ActionEvent e){
if(sendTextArea.getText().length() != 0) {
clientWriter.println(userName + ":" + sendTextArea.getText());
sendTextArea.setText("");
sendTextArea.requestFocus();
} else {
receiveTextArea.append("QQChat Server: You can't input for nothing, " +
"please input something again!\n");
}
}
}
结果如下:
发送文本域显示没问题,消息发送后成功置位空,但是显示文本域结果是这样的:
各个消息间多了个空行。。。和上面用键盘监听的结果正好反过来了~
在getKeyStroke方法参数中加个modifier参数0,
sendTextArea.getInputMap(JComponent.WHEN_FOCUSED).put(
KeyStroke.getKeyStroke('\n', 0),
"send");
就可以正常显示完全没问题了~
后面的modifier如果不加本该默认是0的就是不用加任何修饰符直接单键作为快捷键,但是不知道为何还要手动加0,这是个遗留的小问题,希望自己日后弄更透彻后再回来修改完善本博文,嘻嘻!
总结:第一种键盘监听方法只是插入了个监听器,并无法改变按键在原组件中的原有行为属性,但是加快捷键的方法就可以改变,比如原本enter键是在文本域中换行并移动光标到下一行起始位置,但是你加快捷键后就改变了你在该文本域中按enter键的行为变成你自定义的Action行为了~至于用哪种方法实现,就看你自己取舍吧~