文章目录
P1 聊天室用户端APP功能分析
对于客户端的聊天室APP,其功能不像服务器端APP那么简单,聊天室客户端APP需要有连接,登录,聊天这三个主要模块
对于连接,如果当前聊天室已经满员,那么客户端不能连接到服务器进入聊天室,且应该有相应的对话框提示
对于登录,理应提供登录和注册两个按键,点击登录则应该在数据库查找当前用户是否存在且密码是否正确,均通过才能进入聊天室,如果是新用户那么就需要点击注册按键,注册好自己的账号,昵称,密码,将其插入到数据库表中,再登录进入聊天室
对于聊天室,需要有友好的窗口布局,显示好友列表,系统信息,聊天信息等,还需要支持私聊,群聊等功能,且对于下线等操作都需要一系列的妥善处理
实际效果:
P2 进入聊天室前的准备
1 连接 ChatRoomConnectToServer
进入聊天室之前,需要连接到服务器和输入账号密码,这里先处理连接到服务器的问题
package com.mec.chatroom.client.view;
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import com.mec.csframework.core.Client;
import com.mec.csframework.core.ClientActionAdapter;
import com.my.util.IMyView;
import com.my.util.ViewIsNullException;
import com.my.util.ViewTool;
public class ChatRoomConnectToServer implements IMyView{
private static final int Width = 500;
private static final int Height = 300;
private JFrame jfrmView;
private JLabel jlblConnect;
private int times;
private Client client;
/**
* Client类将IClientAction作为成员,并有相应的set,get方法
* set用来替换接口,get可以用来取得里面的具体方法
* 目的就是为了将一些只有APP开发者才能做的决定留给上层,如怎样处理对端异常掉线
* 这里有继承了ClientActionAdapter的内部类,APP开发者用它实现需要实现的方法
* 再通过setClientAction方法,使得底层能从上层获取如何处理问题
* 如在会话层处理对端异常掉线:this.client.getClientAction().serverAbnormalDrop
*/
public ChatRoomConnectToServer() {
this.times = 0;
this.client = new Client();
this.client.setClientAction(new ConnectToServerAction());
}
/**
* 提供给用户的设置IP的方法
*/
public void setIp(String ip) {
this.client.setIp(ip);
}
/**
* 提供给用户的设置端口的方法
*/
public void setPort(int port) {
this.client.setPort(port);
}
/**
* 初始化连接界面
*/
@Override
public void init() {
this.jfrmView = new JFrame("雫-聊天室-连接服务器");
this.jfrmView.setLayout(new BorderLayout());
this.jfrmView.setSize(Width, Height);
this.jfrmView.setLocationRelativeTo(null);
this.jfrmView.setResizable(false);
this.jfrmView.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
this.jlblConnect = new JLabel("", JLabel.CENTER);
this.jlblConnect.setFont(topicFont);
this.jlblConnect.setForeground(topicColor);
this.jfrmView.add(jlblConnect, BorderLayout.CENTER);
}
/**
* 显示完连接窗口后尝试连接到服务器
*/
@Override
public void afterShowView() {
boolean keepLink = true;
boolean linkOk = false;
while(!linkOk && keepLink) {
//每次连接刷新times的次数
this.jlblConnect.setText("第" + ++this.times + "次连接...");
//connectToServer方法连接成功返回true,失败false
linkOk = this.client.connectToServer();
//如果连接失败,弹出对话框询问用户是否继续尝试连接
if(!linkOk) {
int choice = ViewTool.getUserChoice(jfrmView, "是否继续连接");
keepLink = choice == JOptionPane.YES_OPTION;
}
}
}
/**
* 关闭连接窗口的内部方法
*/
private void closeConnectView() {
try {
this.closeView();
} catch (ViewIsNullException e) {
e.printStackTrace();
}
}
@Override
public void dealAction() {
}
@Override
public JFrame getFrame() {
return this.jfrmView;
}
/**
* 构建一个内部类继承ClientActionAdapter
* 用来选择性的完成连接时需要APP层实现的方法
*/
class ConnectToServerAction extends ClientActionAdapter {
public ConnectToServerAction() {
}
//超出最大连接的处理,显示警告框并关闭连接窗口
@Override
public void serverOutOfRoom() {
ViewTool.showWarnning(jfrmView, "聊天室已满,请稍后尝试进入");
closeConnectView();
}
//连接成功后,应该进入登录窗口,并关闭连接窗口
@Override
public void afterConnectToServer() {
ChatRoomLoginView loginView = new ChatRoomLoginView(client);
loginView.initView();
try {
loginView.showView();
} catch (ViewIsNullException e) {
e.printStackTrace();
}
closeConnectView();
}
}
}
2 登录 ChatRoomLoginView
在连接成功后,进入登录窗口,这里没有做关于数据库表的访问和插入,只是用来演示:
package com.mec.chatroom.client.view;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import com.mec.chatroom.client.model.UserInfo;
import com.mec.csframework.core.Client;
import com.mec.csframework.core.ClientActionAdapter;
import com.my.util.IMyView;
import com.my.util.ViewIsNullException;
import com.my.util.ViewTool;
public class ChatRoomLoginView implements IMyView{
private static final int Width = 400;
private static final int Height = 200;
private JFrame jfrmLogin;
private JTextField jtxtName;
private JPasswordField jpswPassWord;
private JButton jbtnLogin;
private JLabel jlblRegistry;
private Client client;
public ChatRoomLoginView(Client client) {
this.client = client;
this.client.setClientAction(new ChatRoomLoginAction());
}
/**
* 连接成功,进入到登录窗口的初始化
*/
@Override
public void init() {
this.jfrmLogin = new JFrame("雫-聊天室-登录");
this.jfrmLogin.setSize(Width, Height);
this.jfrmLogin.setResizable(false);
this.jfrmLogin.setLocationRelativeTo(null);
this.jfrmLogin.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
this.jfrmLogin.setLayout(new BorderLayout());
JLabel jlblTopic = new JLabel("雫-聊天室-登录");
jlblTopic.setFont(topicFont);
jlblTopic.setForeground(topicColor);
this.jfrmLogin.add(jlblTopic, BorderLayout.NORTH);
JPanel jpnlBody = new JPanel(null);
this.jfrmLogin.add(jpnlBody, BorderLayout.CENTER);
int left = 10 * MARGIN;
int top = 4 * MARGIN;
JLabel jlblName = new JLabel("账号");
jlblName.setFont(normalFont);
jlblName.setBounds(left, top, normalFontSize * 2, normalFontSize + 4);
jpnlBody.add(jlblName);
this.jtxtName = new JTextField();
this.jtxtName.setFont(normalFont);
this.jtxtName.setBounds(left + jlblName.getWidth() + MARGIN, top,
normalFontSize * 12, normalFontSize + 6);
jpnlBody.add(jtxtName);
top += this.jtxtName.getHeight() + 2 * MARGIN;
JLabel jlblPassWord = new JLabel("密码");
jlblPassWord.setFont(normalFont);
jlblPassWord.setBounds(left, top, normalFontSize * 2, normalFontSize + 4);
jpnlBody.add(jlblPassWord);
this.jpswPassWord = new JPasswordField();
this.jpswPassWord.setFont(normalFont);
this.jpswPassWord.setBounds(left + jlblName.getWidth() + MARGIN, top,
normalFontSize * 12, normalFontSize + 6);
jpnlBody.add(jpswPassWord);
JPanel jpnlFooter = new JPanel(new FlowLayout());
this.jfrmLogin.add(jpnlFooter, BorderLayout.SOUTH);
this.jbtnLogin = new JButton("登录");
this.jbtnLogin.setFont(normalFont);
jpnlFooter.add(this.jbtnLogin);
JLabel jlblBlank = new JLabel(" ");
jlblBlank.setFont(normalFont);
jpnlFooter.add(jlblBlank);
this.jlblRegistry = new JLabel("注册");
this.jlblRegistry.setFont(normalFont);
this.jlblRegistry.setForeground(topicColor);
this.jlblRegistry.setCursor(csHand);
jpnlFooter.add(this.jlblRegistry);
}
@Override
public void afterShowView() {
}
/**
* 注册窗口应该响应的窗口事件
*/
@Override
public void dealAction() {
//账号文本框获得焦点时,全选中
this.jtxtName.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
jtxtName.selectAll();
}
});
//在账号文本框敲击回车时,密码框获得焦点
this.jtxtName.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
jpswPassWord.requestFocus();
}
});
//密码框获得焦点时,清空密码框
this.jpswPassWord.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
jpswPassWord.setText("");
}
});
//在密码框敲击回车时,登录按键获得焦点
this.jpswPassWord.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
jbtnLogin.requestFocus();
}
});
//点击右上x关闭窗口时,下线该客户端,断开连接
this.jfrmLogin.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
client.offLine();
}
});
//点击登录按钮,进入聊天室界面
this.jbtnLogin.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String nick = jtxtName.getText().trim();
if(nick == null || nick.length() <= 0) {
ViewTool.showError(jfrmLogin, "账号不能为空");
jtxtName.requestFocus();
return;
}
//账号文本框有文本后,可以进入聊天室窗口
ChatRoomView chatRoomView =
new ChatRoomView(client, new UserInfo(client.getId(), nick));
chatRoomView.initView();
try {
chatRoomView.showView();
} catch (ViewIsNullException e1) {
e1.printStackTrace();
}
//关闭当前登录窗口
closeLoginView();
}
});
}
@Override
public JFrame getFrame() {
return this.jfrmLogin;
}
/**
* 关闭登录窗口的内部方法
*/
private void closeLoginView() {
try {
closeView();
} catch (ViewIsNullException e) {
e.printStackTrace();
}
}
/**
* 构建一个内部类继承ClientActionAdapter
* 用来选择性的完成登录时需要APP层实现的方法
*/
class ChatRoomLoginAction extends ClientActionAdapter {
public ChatRoomLoginAction() {
}
//处理服务器异常掉线,弹出错误,关闭登录窗口并下线
@Override
public void serverAbnormalDrop() {
ViewTool.showError(jfrmLogin, "服务器异常,暂时停止服务");
closeLoginView();
}
//下线前弹出确认是否下线
@Override
public boolean confirmOffLine() {
int choice = ViewTool.getUserChoice(jfrmLogin, "请确认,是否下线");
return choice == JOptionPane.YES_OPTION;
}
//下线后,关闭登录窗口
@Override
public void afterOffLine() {
closeLoginView();
}
//服务器强制下线的提示
@Override
public void serverForceDown() {
ViewTool.showError(jfrmLogin, "服务器被强制宕机,停止服务");
}
//某客户端被服务器下线
@Override
public void killByServer(String reason) {
ViewTool.showError(jfrmLogin, "本机被强制下线,原因" + reason);
closeLoginView();
}
}
}
3 用户信息 UserInfo
对于每个进入聊天室的用户,都需要为其生成一个独有的对象,包含它的id和昵称,以便在发送信息时找到对应的id,且昵称需要显示在好友列表中,发送的信息也应该显示“昵称 + 信息”显示在聊天画板中
package com.mec.chatroom.client.model;
/**
*
* @author coisini1999
*
*/
public class UserInfo {
private String netId;
private String nick;
public UserInfo(String netId, String nick) {
this.netId = netId;
this.nick = nick;
}
public String getNetId() {
return netId;
}
public void setNetId(String netId) {
this.netId = netId;
}
public String getNick() {
return nick;
}
public void setNick(String nick) {
this.nick = nick;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((netId == null) ? 0 : netId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
UserInfo other = (UserInfo) obj;
if (netId == null) {
if (other.netId != null)
return false;
} else if (!netId.equals(other.netId))
return false;
return true;
}
@Override
public String toString() {
return nick;
}
}
P3 聊天窗口的实现 ChatRoomView
当有了UserInfo,成功的连接,登录后,就可以进入聊天室,但聊天室窗口有一系列操作需要完善,如私聊,群聊上线告知所有人,下线将自己在其它好友的好友列表中移除等操作需要完善
1 窗口界面的绘制
/**
* 取得当前窗口,方便交给关闭方法
*/
@Override
public JFrame getFrame() {
return this.jfrmChatView;
}
/**
* 绘制画板标题的方法
*/
private TitledBorder newTitledBorder(String topic, int position, int adjust) {
TitledBorder border = new TitledBorder(topic);
border.setTitleFont(normalFont);
border.setTitlePosition(position);
border.setTitleJustification(adjust);
return border;
}
/**
* 初始化客户端聊天室界面
*/
@Override
public void init() {
this.jfrmChatView = new JFrame("雫 - 聊天室");
this.jfrmChatView.setMinimumSize(new Dimension(MinWidth, MinHeight));
this.jfrmChatView.setExtendedState(JFrame.MAXIMIZED_BOTH);
this.jfrmChatView.setLocationRelativeTo(null);
this.jfrmChatView.setLayout(new BorderLayout());
this.jfrmChatView.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
JLabel jlblTopic = new JLabel("雫", 0);
jlblTopic.setFont(topicFont);
jlblTopic.setForeground(topicColor);
this.jfrmChatView.add(jlblTopic, BorderLayout.NORTH);
this.jpnlBody = new JPanel(null);
this.jfrmChatView.add(this.jpnlBody, BorderLayout.CENTER);
this.jpnlFriend = new JPanel(null);
jpnlBody.add(this.jpnlFriend);
this.jlblWelcome = new JLabel("欢迎");
this.jlblWelcome.setFont(topicFont);
this.jlblWelcome.setForeground(topicColor);
this.jpnlFriend.add(this.jlblWelcome);
this.jlblUserMe = new JLabel(this.userMe.getNick());
this.jlblUserMe.setFont(normalFont);
this.jpnlFriend.add(this.jlblUserMe);
this.dlmFriendList = new DefaultListModel<>();
this.jlstFriendList = new JList<>(this.dlmFriendList);
this.jlstFriendList.setFont(normalFont);
this.jscpFriendList = new JScrollPane(this.jlstFriendList);
this.jscpFriendList.setBorder(newTitledBorder("好友列表", TitledBorder.TOP, TitledBorder.CENTER));
this.jpnlFriend.add(this.jscpFriendList);
this.jpnlChatContext = new JPanel(null);
jpnlBody.add(this.jpnlChatContext);
this.jtatChatContext = new JTextArea();
this.jtatChatContext.setFont(normalFont);
this.jscpChatContext = new JScrollPane(this.jtatChatContext);
this.jscpChatContext.setBorder(newTitledBorder("聊天记录", TitledBorder.TOP, TitledBorder.CENTER));
this.jpnlChatContext.add(this.jscpChatContext);
this.jpnlSystemMessage = new JPanel(null);
jpnlBody.add(this.jpnlSystemMessage);
this.jtatSystemMessage = new JTextArea();
this.jtatSystemMessage.setFont(normalFont);
this.jscpSystemMessage = new JScrollPane(this.jtatSystemMessage);
this.jscpSystemMessage.setBorder(newTitledBorder("系统消息", TitledBorder.TOP, TitledBorder.CENTER));
this.jpnlSystemMessage.add(this.jscpSystemMessage);
JPanel jpnlFooter = new JPanel(new FlowLayout());
this.jfrmChatView.add(jpnlFooter, BorderLayout.SOUTH);
this.jlblCurrentFriend = new JLabel("所有人");
this.jlblCurrentFriend.setFont(normalFont);
jpnlFooter.add(this.jlblCurrentFriend);
this.jtxtChatContent = new JTextField(30);
this.jtxtChatContent.setFont(normalFont);
jpnlFooter.add(this.jtxtChatContent);
}
2 控件采用绝对定位
对于窗口内的画板和标签都采用了绝对定位的方式,但当用户操作窗口大小时,每次都需要重新计算控件的位置
/**
* 部分控件需要采用绝对定位的方法
*/
private void redrawView() {
int bodyWidth = this.jpnlBody.getWidth();
int bodyHeigth = this.jpnlBody.getHeight();
int sideWidth = (int) ((bodyWidth - 4 * MARGIN) * SideScale);
int contextWidth = (int) ((bodyWidth - 4 * MARGIN) * ContentScale);
int top = MARGIN;
int left = MARGIN;
this.jpnlFriend.setBounds(left, top, sideWidth, bodyHeigth);
left += MARGIN + sideWidth;
this.jpnlChatContext.setBounds(left, top, contextWidth, bodyHeigth);
left += MARGIN + contextWidth;
this.jpnlSystemMessage.setBounds(left, top, sideWidth, bodyHeigth);
top = MARGIN;
int welcomeWidth = this.jlblWelcome.getText().length() * topicFontSize;
this.jlblWelcome.setBounds((sideWidth - welcomeWidth) / 2, top, welcomeWidth, topicFontSize);
top += this.jlblWelcome.getHeight() + MARGIN;
int userMeWidth = this.jlblUserMe.getText().length() * normalFontSize;
this.jlblUserMe.setBounds((sideWidth - userMeWidth) / 2, top, userMeWidth, normalFontSize);
int friendListHeight = bodyHeigth - 5 * MARGIN - this.jlblWelcome.getHeight() - this.jlblUserMe.getHeight();
top += this.jlblUserMe.getHeight() + 2 * MARGIN;
this.jscpFriendList.setBounds(0, top, sideWidth, friendListHeight);
this.jscpSystemMessage.setBounds(0, 0, sideWidth, bodyHeigth - MARGIN);
this.jscpChatContext.setBounds(0, 0, contextWidth, bodyHeigth - MARGIN);
}
且窗口显示后后还需要一系列操作,如向当前在线用户发送我上线了的信息
3 APP层的信息描述
在聊天室,最基础的功能就是发送信息,所以仍然需要采用“信令” + “信息”来描述,虽然CSFramework也有一套“信令” + “信息”,但那是处理框架内的信息的,主要是确定信息的目的和接收者,而这里的“信令” + “信息”才是为具体的聊天信息服务
(1) APP层的信令 EChatCommand
(2) APP层的信息描述 ChatMessage
package com.mec.chatroom.core;
public class ChatMessage {
private EChatCommand command;
private String from;
private String to;
private String message;
public ChatMessage() {
}
public EChatCommand getCommand() {
return command;
}
public ChatMessage setCommand(EChatCommand command) {
this.command = command;
return this;
}
public String getFrom() {
return from;
}
public ChatMessage setFrom(String from) {
this.from = from;
return this;
}
public String getTo() {
return to;
}
public ChatMessage setTo(String to) {
this.to = to;
return this;
}
public String getMessage() {
return message;
}
public ChatMessage setMessage(String message) {
this.message = message;
return this;
}
@Override
public String toString() {
return "ChatMessage [command=" + command + ", from=" + from + ", to=" + to + ", message=" + message + "]";
}
}
4 刚上线需要的操作
/**
* 显示完聊天窗口后的善后处理
* 和必要操作,即上线后给所有人发送一条我上线了的信息
*/
@Override
public void afterShowView() {
this.jtatChatContext.setEditable(false);
this.jtatChatContext.setFocusable(false);
this.jtatSystemMessage.setEditable(false);
this.jtatSystemMessage.setFocusable(false);
this.dlmFriendList.addElement(friendAll);
this.jlstFriendList.setSelectedIndex(0);
this.jlblCurrentFriend.setText(friendAll.getNick());
this.jlblCurrentFriend.setName(friendAll.getNetId());
this.jtxtChatContent.requestFocus();
//下面的代码意为向所有在线的人发送一条我上线了的信息
//信息载体是ChatMessage,但是底层工具采用的send方法内部是writeUTF(String xxx)
//所以要将ChatMessage类的对象转换成String类的对象,以便能够通过网络传输
//对于这里的setMessage内的内容,是将发送者UserInfo类型的userMe转换成字符串发送
//因为需要获取到发送者的nick
//所以对于该信息的处理,要分两步,1,将String类的chatMessage转换成ChatMessage
//2,将chatMessage内的message由String类型转换成UserInfo类型
ChatMessage chatMessage = new ChatMessage()
.setCommand(EChatCommand.HELLO)
.setFrom(userMe.getNetId())
.setTo(friendAll.getNetId())
.setMessage(Client.gson.toJson(userMe));
this.client.toOther(Client.gson.toJson(chatMessage));
}
5 完成框架抛给APP层需要实现的方法
/**
* 内部类,用来实现ClientActionAdapter内的方法
* 将下层无权处理的信息交给上层APP开发者完成
*/
class ChatAction extends ClientActionAdapter {
public ChatAction() {
}
//接收到私聊传信息的具体处理
@Override
public void toOne(String source, String message) {
ChatMessage chatMessage = Client.gson.fromJson(message, ChatMessage.class);
EChatCommand command = chatMessage.getCommand();
String mess = chatMessage.getMessage();
switch(command) {
//向刚上线的客户端发送本客户端在线的信息
case HELLO:
//解析chatMessage内的Message,取得向本客户端发送在线信息的发送者的nick
String cMessage = chatMessage.getMessage();
UserInfo friendOnline = Client.gson.fromJson(cMessage, UserInfo.class);
dlmFriendList.addElement(friendOnline);
break;
case SPEACH:
//解析发送到本客户端的私聊信息,获取发送人的详细信息
UserInfo user = getUserById(chatMessage.getFrom());
showChatContent(user + "对你悄悄说:" + mess);
break;
default:
break;
}
}
//接收到来自群发信息的具体处理
@Override
public void toOther(String sourceId, String message) {
//将接收到的String类的chatMessage转换成ChatMessage类的对象,并取出command分类处理
ChatMessage chatMessage = Client.gson.fromJson(message, ChatMessage.class);
EChatCommand command = chatMessage.getCommand();
String mess = chatMessage.getMessage();
switch(command) {
//一个刚上线的客户端向所有客户端发送上线信息
case HELLO:
UserInfo newFriend = Client.gson.fromJson(mess, UserInfo.class);
dlmFriendList.addElement(newFriend);
showSystemMessage("好友" + newFriend + "上线");
//告知刚上线的客户端自己在线
String targetId = newFriend.getNetId();
ChatMessage cMessage = new ChatMessage()
.setCommand(EChatCommand.HELLO)
.setFrom(userMe.getNetId())
.setTo(targetId)
.setMessage(Client.gson.toJson(userMe));
client.toOne(targetId, Client.gson.toJson(cMessage));
break;
//一个下线了的客户端向所有客户端发送下线信息
case BYE_BYE:
UserInfo byebyeFriend = Client.gson.fromJson(mess, UserInfo.class);
dlmFriendList.removeElement(byebyeFriend);
showSystemMessage("好友" + byebyeFriend + "下线");
break;
case SPEACH:
//解析发送到本客户端的私聊信息,获取发送人的详细信息
UserInfo user = getUserById(chatMessage.getFrom());
showChatContent(user + "对所有人说:" + mess);
break;
default:
break;
}
}
//下线前先确认是否真的要下线了
@Override
public boolean confirmOffLine() {
int choice = ViewTool.getUserChoice(jfrmChatView, "是否确认离开?");
return choice == JOptionPane.YES_OPTION;
}
//确认下线后,向所有人发送我离开了的信息
@Override
public void beforeOffLine() {
ChatMessage chatMessage = new ChatMessage()
.setCommand(EChatCommand.BYE_BYE)
.setFrom(userMe.getNetId())
.setTo(FRIEND_ALL_NET_ID)
.setMessage(Client.gson.toJson(userMe));
client.toOther(Client.gson.toJson(chatMessage));
}
//发送完离开信息后,关闭聊天室
@Override
public void afterOffLine() {
closeChatRoomView();
//这里提供了一种关闭的操作,强制退出,结束JVM的执行
//但是有些系统资源可能无法释放,因此这种方式应该慎用
// System.exit(0);
}
// 服务器强制下线处理
@Override
public void serverForceDown() {
ViewTool.showError(jfrmChatView, "聊天室服务器异常宕机,服务终止");
closeChatRoomView();
}
//服务器异常宕机处理
@Override
public void serverAbnormalDrop() {
ViewTool.showError(jfrmChatView, "聊天室服务器被强制宕机,服务终止");
closeChatRoomView();
}
// 服务器强制下线某客户端的处理
@Override
public void killByServer(String reason) {
ViewTool.showError(jfrmChatView, "您被强制退出聊天室,服务停止 \n"
+ "原因:" + reason );
closeChatRoomView();
}
}
6 响应用户的操作
/**
* 对用户操作的响应
*/
@Override
public void dealAction() {
//点击关闭窗口
this.jfrmChatView.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
client.offLine();
}
});
//用户每次调整窗口时,都会重新计算控件的位置
this.jfrmChatView.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
redrawView();
}
});
//用户选择好友列表时,在聊天文本框前显示好友
this.jlstFriendList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if(e.getValueIsAdjusting() == true) {
setSelectedFriend();
}
}
});
//用户在聊天框内输入内容,点击回车发送
this.jtxtChatContent.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String chatContent = jtxtChatContent.getText().trim();
if(chatContent.length() <= 0) {
ViewTool.showWarnning(jfrmChatView, "聊天内容不能为空");
jtxtChatContent.setText("");
return;
}
//获取当前好友的netId,区分私聊和群聊
String targetId = jlblCurrentFriend.getName();
if(targetId.equals(FRIEND_ALL_NET_ID)) {
//群聊过程
ChatMessage chatMessage = new ChatMessage()
.setCommand(EChatCommand.SPEACH)
.setFrom(userMe.getNetId())
.setTo(FRIEND_ALL_NET_ID)
.setMessage(chatContent);
client.toOther(Client.gson.toJson(chatMessage));
showChatContent("你对所有人说:" + chatContent);
} else {
//私聊过程,先判断私聊的好友是否在线
if(isUserExit(targetId)) {
ChatMessage chatMessage = new ChatMessage()
.setCommand(EChatCommand.SPEACH)
.setFrom(userMe.getNetId())
.setTo(targetId)
.setMessage(chatContent);
client.toOne(targetId, Client.gson.toJson(chatMessage));
showChatContent("你对" + jlblCurrentFriend.getText() + "悄悄说:" +chatContent);
} else {
ViewTool.showWarnning(jfrmChatView, "当前好友" + jlblCurrentFriend.getText() + "已下线");
jlstFriendList.setSelectedIndex(0);
setSelectedFriend();
}
}
jtxtChatContent.setText("");
}
});
}