文章目录

  • P1 聊天室用户端APP功能分析
  • P2 进入聊天室前的准备
    • 1 连接 ChatRoomConnectToServer
    • 2 登录 ChatRoomLoginView
    • 3 用户信息 UserInfo
  • P3 聊天窗口的实现 ChatRoomView
    • 1 窗口界面的绘制
    • 2 控件采用绝对定位
    • 3 APP层的信息描述
      • (1) APP层的信令 EChatCommand
      • (2) APP层的信息描述 ChatMessage
    • 4 刚上线需要的操作
    • 5 完成框架抛给APP层需要实现的方法
    • 6 响应用户的操作

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);}/*** 初始化连接界面*/@Overridepublic 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);}/*** 显示完连接窗口后尝试连接到服务器*/@Overridepublic void afterShowView() {boolean keepLink = true;boolean linkOk = false;while(!linkOk && keepLink) {//每次连接刷新times的次数this.jlblConnect.setText("第" + ++this.times + "次连接...");//connectToServer方法连接成功返回true,失败falselinkOk = 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();}}@Overridepublic void dealAction() {}@Overridepublic JFrame getFrame() {return this.jfrmView;}/*** 构建一个内部类继承ClientActionAdapter* 用来选择性的完成连接时需要APP层实现的方法*/class ConnectToServerAction extends ClientActionAdapter {public ConnectToServerAction() {}//超出最大连接的处理,显示警告框并关闭连接窗口@Overridepublic void serverOutOfRoom() {ViewTool.showWarnning(jfrmView, "聊天室已满,请稍后尝试进入");closeConnectView();}//连接成功后,应该进入登录窗口,并关闭连接窗口@Overridepublic 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());}/*** 连接成功,进入到登录窗口的初始化*/@Overridepublic 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);}@Overridepublic void afterShowView() {   }/*** 注册窗口应该响应的窗口事件*/@Overridepublic void dealAction() {//账号文本框获得焦点时,全选中this.jtxtName.addFocusListener(new FocusAdapter() {@Overridepublic void focusGained(FocusEvent e) {jtxtName.selectAll();}});//在账号文本框敲击回车时,密码框获得焦点this.jtxtName.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {jpswPassWord.requestFocus();}});//密码框获得焦点时,清空密码框this.jpswPassWord.addFocusListener(new FocusAdapter() {@Overridepublic void focusGained(FocusEvent e) {jpswPassWord.setText("");}});//在密码框敲击回车时,登录按键获得焦点this.jpswPassWord.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {jbtnLogin.requestFocus();}});//点击右上x关闭窗口时,下线该客户端,断开连接this.jfrmLogin.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {client.offLine();}});    //点击登录按钮,进入聊天室界面this.jbtnLogin.addActionListener(new ActionListener() {@Overridepublic 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();}});}@Overridepublic JFrame getFrame() {return this.jfrmLogin;}/*** 关闭登录窗口的内部方法*/private void closeLoginView() {try {closeView();} catch (ViewIsNullException e) {e.printStackTrace();}}/*** 构建一个内部类继承ClientActionAdapter* 用来选择性的完成登录时需要APP层实现的方法*/class ChatRoomLoginAction extends ClientActionAdapter {public ChatRoomLoginAction() {}//处理服务器异常掉线,弹出错误,关闭登录窗口并下线@Overridepublic void serverAbnormalDrop() {ViewTool.showError(jfrmLogin, "服务器异常,暂时停止服务");closeLoginView();}//下线前弹出确认是否下线@Overridepublic boolean confirmOffLine() {int choice = ViewTool.getUserChoice(jfrmLogin, "请确认,是否下线");return choice == JOptionPane.YES_OPTION;}//下线后,关闭登录窗口@Overridepublic void afterOffLine() {closeLoginView();}//服务器强制下线的提示@Overridepublic void serverForceDown() {ViewTool.showError(jfrmLogin, "服务器被强制宕机,停止服务");}//某客户端被服务器下线@Overridepublic 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;}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + ((netId == null) ? 0 : netId.hashCode());return result;}@Overridepublic 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;}@Overridepublic String toString() {return nick;}}

P3 聊天窗口的实现 ChatRoomView

当有了UserInfo,成功的连接,登录后,就可以进入聊天室,但聊天室窗口有一系列操作需要完善,如私聊,群聊上线告知所有人,下线将自己在其它好友的好友列表中移除等操作需要完善

1 窗口界面的绘制

 /*** 取得当前窗口,方便交给关闭方法*/@Overridepublic 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;}/*** 初始化客户端聊天室界面*/@Overridepublic 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;}@Overridepublic String toString() {return "ChatMessage [command=" + command + ", from=" + from + ", to=" + to + ", message=" + message + "]";}}

4 刚上线需要的操作

 /*** 显示完聊天窗口后的善后处理* 和必要操作,即上线后给所有人发送一条我上线了的信息*/@Overridepublic 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() {}//接收到私聊传信息的具体处理@Overridepublic 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,取得向本客户端发送在线信息的发送者的nickString 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;}}//接收到来自群发信息的具体处理@Overridepublic 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;}}//下线前先确认是否真的要下线了@Overridepublic boolean confirmOffLine() {int choice = ViewTool.getUserChoice(jfrmChatView, "是否确认离开?");return choice == JOptionPane.YES_OPTION;}//确认下线后,向所有人发送我离开了的信息 @Overridepublic 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));}//发送完离开信息后,关闭聊天室@Overridepublic void afterOffLine() {closeChatRoomView();//这里提供了一种关闭的操作,强制退出,结束JVM的执行//但是有些系统资源可能无法释放,因此这种方式应该慎用
//          System.exit(0);}// 服务器强制下线处理@Overridepublic void serverForceDown() {ViewTool.showError(jfrmChatView, "聊天室服务器异常宕机,服务终止");closeChatRoomView();}//服务器异常宕机处理  @Overridepublic void serverAbnormalDrop() {ViewTool.showError(jfrmChatView, "聊天室服务器被强制宕机,服务终止");closeChatRoomView();}// 服务器强制下线某客户端的处理@Overridepublic void killByServer(String reason) {ViewTool.showError(jfrmChatView, "您被强制退出聊天室,服务停止 \n"+ "原因:" + reason );closeChatRoomView();}    }

6 响应用户的操作

 /*** 对用户操作的响应*/@Overridepublic void dealAction() {//点击关闭窗口this.jfrmChatView.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {client.offLine();}});//用户每次调整窗口时,都会重新计算控件的位置this.jfrmChatView.addComponentListener(new ComponentAdapter() {@Overridepublic void componentResized(ComponentEvent e) {redrawView();}});//用户选择好友列表时,在聊天文本框前显示好友this.jlstFriendList.addListSelectionListener(new ListSelectionListener() {@Overridepublic void valueChanged(ListSelectionEvent e) {if(e.getValueIsAdjusting() == true) {setSelectedFriend();}}});//用户在聊天框内输入内容,点击回车发送this.jtxtChatContent.addActionListener(new ActionListener() {@Overridepublic 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("");}});}

JAVA SE 实战篇 C7 基于CSFramework的聊天室 (下) 客户端APP相关推荐

  1. [零基础学JAVA]Java SE实战开发-37.MIS信息管理系统实战开发[JDBC](1)

    MIS信息管理系统实战开发之使用MySQL实现保存 开发背景 ID.姓名.年龄为公共信息,而学生有成绩,工人有工资 定义一个抽象类Person(ID.姓名.年龄),学生是其子类,有成绩,工人是其子类有 ...

  2. java实现linkstring,【JAVA SE基础篇】32.String类入门

    [JAVA SE基础篇]32.String类入门 1.字符串 1.String类又称作不可变字符序列 2.String位于java.lang包中,java程序默认导入java.lang包下所有的类 3 ...

  3. java 中间容器 表格_【JAVA SE基础篇】45.迭代器、Collections工具类以及使用容器存储表格...

    本文将要为您介绍的是[JAVA SE基础篇]45.迭代器.Collections工具类以及使用容器存储表格,具体完成步骤: 1.迭代器 迭代器为我们提供了统一遍历容器(List/Map/Set)的方式 ...

  4. [零基础学JAVA]Java SE实战开发-37.MIS信息管理系统实战开发[文件保存](1)

    MIS信息管理系统实战开发之单独使用文件实现保存 开发背景 ID.姓名.年龄为公共信息,而学生有成绩,工人有工资 定义一个抽象类Person(ID.姓名.年龄),学生是其子类,有成绩,工人是其子类有工 ...

  5. 《Java SE实战指南》22-04:字节流和字符流

    <Java SE实战指南> 22-04:字节流和字符流 内容导航: 前言 1.分类定义 2.字符流常用类 2.1.FileWriter 2.2.FileReader 前言 我们每天都在使用 ...

  6. java se运算符优先级,【JAVA SE基础篇】10.运算符优先级与类型转换

    [JAVA SE基础篇]10.运算符优先级与类型转换 1.运算符的优先级 运算符的优先级在考试中会考,了解即可,多用就会熟能生巧 实际使用过程中建议用小括号来分优先级 关键就是:逻辑非>逻辑与& ...

  7. java开发websocket聊天室_java实现基于websocket的聊天室

    [实例简介] java实现基于websocket的聊天室 [实例截图] [核心代码] chatMavenWebapp └── chat Maven Webapp ├── pom.xml ├── src ...

  8. Java Socket通信实现多人多端网络画板聊天室

    老规矩,先上实现的效果展示! Java Socket通信实现多人多端网络画板聊天室 本文介绍了一个基于Socket实现网络画板聊天室的完整过程,聊天室具备多人文本对话.同步绘图等功能. 初尝试 Soc ...

  9. SSM(五)基于webSocket的聊天室

    SSM(五)基于webSocket的聊天室 前言 不知大家在平时的需求中有没有遇到需要实时处理信息的情况,如站内信,订阅,聊天之类的.在这之前我们通常想到的方法一般都是采用轮训的方式每隔一定的时间向服 ...

最新文章

  1. NET中winform与webform互相通讯实例,CS调用BS页面的JS函数---转载
  2. Spring 自动装配模式之byType
  3. SAP C4C里收藏了的客户,在什么地方能够快捷打开
  4. DFS——深度优先搜索基础
  5. 在 VC++ 中使用 内联汇编
  6. 基于知识引入的情感分析
  7. ASP.NET 中的 SQL Server 应用服务数据库角色和视图
  8. mysql_store_result和mysql_use_result
  9. 2021李宏毅机器学习笔记
  10. SpringBoot+JWT+SpringSecurity对api进行授权保护
  11. 视频动作识别调研(Action Recognition)
  12. vue封装了个日历组件(包含农历,节日)
  13. Promise的含义和用法
  14. 《剑指 Offer》题目汇总
  15. 分析一个简单的汇编代码
  16. JAVA通过Hutool解析CSV文件【导入即用,无需封装】
  17. 上海科技大学计算机浙江分数线,2018上海科技大学录取分数线
  18. Cobalt Strike客户端连接被拒绝
  19. 统计给定字符串中各字符的个数
  20. tomcat 7 最新版本 apache-tomcat-7.0.109

热门文章

  1. 高通耳机阻抗估算流程
  2. 避免当野monkey, 走野路子
  3. 万级并发服务器内核调优总结
  4. (八十一)探索hidl-gen使用及IWifi.hal 实现
  5. watch监听和computed计算属性的使用和区别
  6. 实现 Trie (前缀树)
  7. 从码农到工程师:只要做到这6点
  8. 节假日读取接口_节假日API接口,2018年,直接计算好的
  9. 读《人性的优点》有感
  10. iOS开发:GitHub上传代码错误提示fatal: Authentication failed for 'https://gitee.com/XXX/XXX.git/‘的解决方法