JavaGUI--模拟QQ聊天界面私聊群聊
點擊下載源代碼
目錄
JFrame類的幾個重要方法
持久層
服務(wù)端創(chuàng)建
用戶注冊模塊
用戶登錄窗口
好友列表和群聊列表模塊
用戶私聊邏輯
群聊創(chuàng)建
群聊消息
JFrame類的幾個重要方法
- Frame類的構(gòu)造方法,設(shè)置所畫出的窗口的名稱:public JFrame(String title)
- 設(shè)置該窗口下的所有窗體(panel):public void setContentPane(Container contentPane)
- 設(shè)置窗口關(guān)閉之后的狀態(tài):public void setDefaultCloseOperation(int operation)
- 設(shè)置窗口顯示位置(傳入為null時則默認(rèn)顯示在屏幕正中央):public void setLocationRelativeTo(Component c)
- 根據(jù)布局來確定窗口的最佳大小 public void pack()
- 設(shè)置頁面是否顯示(默認(rèn)不顯示):public void setVisible(boolean b)
持久層
- 創(chuàng)建User對象
- 創(chuàng)建持久層層基礎(chǔ)類,封裝數(shù)據(jù)源,獲取連接,關(guān)閉資源等操作
- 創(chuàng)建用戶持久層模塊
用戶對象和數(shù)據(jù)庫對應(yīng)
package com.lin.client.entity;import java.util.Set;/*** Description:* Author: llf* Created in 2019/8/23 16:30*/ public class User {private Integer id;private String userName;private String password;private String brief;private Set<String> userNames;public User() {}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getBrief() {return brief;}public void setBrief(String brief) {this.brief = brief;}public Set<String> getUserNames() {return userNames;}public void setUserNames(Set<String> userNames) {this.userNames = userNames;} }dao層基類
package com.lin.client.dao;import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidDataSourceFactory; import com.alibaba.druid.pool.DruidPooledConnection; import com.lin.client.entity.User; import com.lin.util.CommUtils;import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties;/*** Description:dao層基礎(chǔ)類i,封裝數(shù)據(jù)源,獲取連接,關(guān)閉資源等操作* Author: llf* Created in 2019/8/23 16:30*/ public class BasedDao {//獲取數(shù)據(jù)源private static DruidDataSource dataSource;static{Properties properties= CommUtils.loadProperties("db.properties");try {dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {System.out.println("獲取數(shù)據(jù)源失敗");e.printStackTrace();}}//獲取連接protected DruidPooledConnection getConnection() {try {return (DruidPooledConnection) dataSource.getPooledConnection();} catch (SQLException e) {System.out.println("數(shù)據(jù)庫連接失敗");e.printStackTrace();}return null;}//關(guān)閉資源protected void closeResources(Connection connection,Statement statement) {try {if (connection != null) connection.close();if (statement != null) statement.close();} catch (SQLException e) {e.printStackTrace();}}protected void closeResources(Connection connection,Statement statement,ResultSet resultSet) {closeResources(connection,statement);if (resultSet != null) {try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}}}用戶查詢和插入DAO模塊
package com.lin.client.dao;import com.alibaba.druid.pool.DruidPooledConnection; import com.lin.client.entity.User; import org.apache.commons.codec.digest.DigestUtils;import java.sql.*;/*** Description:* Author: llf* Created in 2019/8/23 16:30*/ public class AccountDao extends BasedDao{public User userLogin(String userName, String password) {Connection connection=null;PreparedStatement statement=null;ResultSet resultSet=null;try{connection=getConnection();String sql="select * from user where username=? and password=?";statement = connection.prepareStatement(sql);statement.setString(1,userName);//******解密 ********************statement.setString(2, DigestUtils.md5Hex(password));resultSet=statement.executeQuery();if (resultSet.next()) {User user=getUser(resultSet);return user;}}catch (SQLException e) {System.out.println("用戶登陸失敗");e.printStackTrace();}finally {closeResources(connection,statement,resultSet);}return null;}private User getUser(ResultSet resultSet) throws SQLException {User user=new User();user.setId(resultSet.getInt("id"));user.setUserName(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));user.setBrief(resultSet.getString("brief"));return user;}public boolean userReg(User user) {Connection connection=null;PreparedStatement statement=null;try{connection = getConnection();String str="insert into user(username,password,brief) values (?,?,?)";//接收執(zhí)行sql語句影響的行數(shù),statement=connection.prepareStatement(str, Statement.RETURN_GENERATED_KEYS);statement.setString(1,user.getUserName());//加密 *************statement.setString(2,DigestUtils.md5Hex(user.getPassword()));statement.setString(3,user.getBrief());int rows = statement.executeUpdate();if (rows==1) {return true;}}catch (SQLException ex) {System.out.println("用戶注冊失敗");ex.printStackTrace();}finally {closeResources(connection,statement);}return false;} }服務(wù)端創(chuàng)建
- 設(shè)置工具類,讀取配置文件中的IP和PORT
- 設(shè)置Json格式和Object類之間的相互轉(zhuǎn)換
- 設(shè)置客戶端和服務(wù)端之間通信的VO對象
創(chuàng)建客戶端和服務(wù)端之間通信的VO對象
public class MessageVO {/*** 客戶端和服務(wù)器之間的協(xié)議*/private String type;/*** 客戶端與服務(wù)端之間發(fā)送數(shù)據(jù)的內(nèi)容*/private String content;/*** 客戶端告訴服務(wù)器要將信息發(fā)送給何處,在群聊中也可封裝群聊用戶集合等數(shù)據(jù)*/private String to;public String getType() {return type;}public void setType(String type) {this.type = type;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public String getTo() {return to;}public void setTo(String to) {this.to = to;} }創(chuàng)建服務(wù)端
public class MultiThreadServer {private static final String IP;private static final int PORT;static {//讀取socket配置文件Properties pros= CommUtils.loadProperties("socket.properties");IP=pros.getProperty("address");PORT=Integer.parseInt(pros.getProperty("port"));}public static void main(String[] args) throws IOException {//創(chuàng)建服務(wù)端ServerSocket serverSocket=new ServerSocket(PORT);//創(chuàng)建線程池設(shè)置連接服務(wù)端的人數(shù)ExecutorService executors = Executors.newFixedThreadPool(50);for (int i = 0; i < 50; i++) {System.out.println("等待客戶端連接...");Socket client = serverSocket.accept();System.out.println("有新的客戶端連接,端口號為"+client.getPort());//有客戶端連接就創(chuàng)建線程不斷監(jiān)聽該客戶端發(fā)來的信息executors.submit(new ExecuteClient(client));}} }封裝客戶端與服務(wù)端的連接
public class Connect2Server {private static final String IP;private static final int PORT;static {Properties properties = CommUtils.loadProperties("socket.properties");IP=properties.getProperty("address");PORT= Integer.parseInt(properties.getProperty("port"));}private Socket client;private InputStream in;private OutputStream out;public Connect2Server() {try {//連接服務(wù)器的端口,所以此處與服務(wù)器端讀取同一個配置文件client=new Socket(IP,PORT);//獲取連接的輸入流in=client.getInputStream();//獲取連接的輸出流out=client.getOutputStream();} catch (IOException e) {e.printStackTrace();}}public InputStream getIn() {return in;}public OutputStream getOut() {return out;} }用戶注冊模塊
創(chuàng)建GUI Form畫布(窗口):設(shè)置相應(yīng)的窗體名稱,Idea軟件會為我們自動生成窗口的對應(yīng)的屬性,
分析
當(dāng)點擊注冊按鈕之后獲取窗體數(shù)據(jù),封裝成User對象,調(diào)用數(shù)據(jù)庫進行創(chuàng)建,
? ? ? ? 創(chuàng)建成功,顯示創(chuàng)建成功,創(chuàng)建用戶窗口關(guān)閉
? ? ? ? 創(chuàng)建失敗,顯示失敗信息
public class UserReg {private JTextField userNameText;private JPasswordField passwordText;private JTextField brifeText;private JButton regBtn;private JPanel userRegPanel;private AccountDao accountDao=new AccountDao();public UserReg() {JFrame frame = new JFrame("用戶注冊");//包含這個窗口下的所有窗體frame.setContentPane(userRegPanel);//設(shè)置關(guān)閉即該窗口永久消失frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//使窗口顯示在正中央frame.setLocationRelativeTo(null);//根據(jù)布局來確定窗口的最佳大小frame.pack();//窗口圖形界面可視, 默認(rèn)不可視frame.setVisible(true);//點擊注冊按鈕,將信息持久化到db中,成功彈出提示框regBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//獲取表單參數(shù)String userName = userNameText.getText();//獲取加密的密碼String password = String.valueOf(passwordText.getPassword());String brief=brifeText.getText();//封裝User保存到數(shù)據(jù)庫中User user=new User();user.setUserName(userName);user.setPassword(password);user.setBrief(brief);if (accountDao.userReg(user)) {//注冊成功JOptionPane.showMessageDialog(frame,"注冊成功!","提示信息",JOptionPane.INFORMATION_MESSAGE);frame.setVisible(false);}else {//注冊失敗JOptionPane.showMessageDialog(frame,"注冊失敗!","錯誤信息",JOptionPane.ERROR_MESSAGE);}}});} }用戶登錄窗口
用畫布畫出該窗口
分析
用戶在登陸界面當(dāng)點擊注冊按鈕時,創(chuàng)建注冊按鈕,跳轉(zhuǎn)到注冊頁面邏輯
用戶在登陸頁面點擊登陸按鈕時,獲取窗體數(shù)據(jù),調(diào)用數(shù)據(jù)庫根據(jù)密碼和用戶名查詢是否存在該用戶
? ? ? ? ?若存在:提示登陸成功信息,
? ? ? ? ? ? ?? ? ? ? ? ?點擊確定,創(chuàng)建連接,將自己的信息以協(xié)議“1”發(fā)送到服務(wù)端,標(biāo)識為登陸邏輯,方便服務(wù)端轉(zhuǎn)向,
? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? * 此時?type = 1? ? ?content? = userName
? ? ? ? ? ? ? ? ? ? ? ? 登陸頁面關(guān)閉,創(chuàng)建用戶列表界面
? ? ? ??若不存在
? ? ? ? ? ? ? ? ? ? ? ??提示錯誤信息即可
package com.lin.client.service;import com.lin.client.dao.AccountDao; import com.lin.client.entity.User; import com.lin.util.CommUtils; import com.lin.vo.MessageVO;import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.util.Scanner; import java.util.Set;/*** Description:登陸成功,將當(dāng)前用戶所有的在線好友發(fā)送到服務(wù)器* Author: llf* Created in 2019/8/23 19:04*/ public class UserLogin {private JButton regButton;private JButton loginButton;private JTextField userNameText;private JPasswordField passwordText;private JPanel userPanel;private JPanel UserLoginPanel;private JFrame frame;private AccountDao accountDao=new AccountDao();public UserLogin() {frame = new JFrame("用戶登陸");frame.setContentPane(UserLoginPanel);//退出即關(guān)閉該對象frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//將窗口置于屏幕中央frame.setLocationRelativeTo(null);//根據(jù)頁面布局設(shè)置窗口的大小frame.pack();//圖形界面可視frame.setVisible(true);//創(chuàng)建監(jiān)聽事件,注冊按鈕,當(dāng)點擊該按鈕,創(chuàng)建出一個注冊頁面regButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {new UserReg();}});//創(chuàng)建監(jiān)聽事件,登陸按鈕loginButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//獲取表單參數(shù) 校驗用戶信息String userName = userNameText.getText();String password = String.valueOf(passwordText.getPassword());User user = accountDao.userLogin(userName,password);if (user!=null) {//顯示成功信息**************JOptionPane.showMessageDialog(frame,"登陸成功","提示信息",JOptionPane.INFORMATION_MESSAGE);//登陸頁面關(guān)閉frame.setVisible(false);/*** 與服務(wù)器建立連接* 讀取服務(wù)端所有好友信息* 新建一個后臺進程不斷讀取服務(wù)器發(fā)來的信息* type 1* content userName* to 私聊 群聊*///以協(xié)議1的方式向服務(wù)器發(fā)送數(shù)據(jù)Connect2Server connect2Server=new Connect2Server();MessageVO msg2Server=new MessageVO();msg2Server.setType("1");msg2Server.setContent(userName);String json2Server=CommUtils.object2Json(msg2Server);try {PrintStream out=new PrintStream(connect2Server.getOut(),true,"UTF-8");//發(fā)送數(shù)據(jù)到服務(wù)器out.println(json2Server);//接收服務(wù)器發(fā)來的消息,顯示所有在線好友,加載用戶列表/*** type 1* content 在線用戶信息* to*/Scanner in=new Scanner(connect2Server.getIn());if (in.hasNextLine()) {String msgFromServerStr = in.nextLine();MessageVO msgFromServer=(MessageVO) CommUtils.json2Object(msgFromServerStr,MessageVO.class);//將服務(wù)器發(fā)送過來的信息封裝成一個儲存user對象的Set集合,傳入好友列表進行顯示,Set<String> users = (Set<String>) CommUtils.json2Object(msgFromServer.getContent(), Set.class);System.out.println("所有在線用戶為:" + users);//加載用戶列表界面,將當(dāng)前用戶名,所有在線好友,與服務(wù)器建立連接,傳遞到好友列表界面***********new FriendsList(userName,users,connect2Server);}} catch (UnsupportedEncodingException ex) {ex.printStackTrace();}}else {//登陸失敗,留在當(dāng)前登陸頁面,顯示錯誤信息JOptionPane.showMessageDialog(frame,"登陸失敗!","錯誤信息",JOptionPane.ERROR_MESSAGE);}}});}public static void main(String[] args) {UserLogin userLogin=new UserLogin();} }服務(wù)器端程序
?//type:“1”?
?//content 所有在線好友? ? ?
//緩存當(dāng)前服務(wù)器所有在線的客戶信息private static Map<String,Socket> clients=new ConcurrentHashMap<>();private static class ExecuteClient implements Runnable{private Socket client;private Scanner in;private PrintStream out;public ExecuteClient(Socket client) {this.client=client;try {this.in=new Scanner(client.getInputStream());this.out=new PrintStream(client.getOutputStream(),true,"UTF-8");} catch (IOException e) {e.printStackTrace();}}@Overridepublic void run() {while (true) {if (in.hasNextLine()) {String jsonStrFromClient = in.nextLine();MessageVO msgFromClient = (MessageVO) CommUtils.json2Object(jsonStrFromClient, MessageVO.class);if (msgFromClient.getType().equals("1")) {//獲取客戶發(fā)來的用戶名String userName=msgFromClient.getContent();/*** 將當(dāng)前所有在線用戶發(fā)送給新用戶* 將新用戶上限信息發(fā)送給其他用戶(上線提醒)* 保存新用戶上線信息*///將當(dāng)前在線的所有用戶發(fā)回客戶端MessageVO msg2Client=new MessageVO();msg2Client.setType("1");msg2Client.setContent(CommUtils.object2Json(clients.keySet()));out.println(CommUtils.object2Json(msg2Client));//將新上線的用戶信息發(fā)回給當(dāng)前以在線的所有用戶sendUserLogin("newLogin:"+userName);//將當(dāng)前用戶注冊到服務(wù)端緩存clients.put(userName,client);System.out.println(userName+"上線了!");System.out.println("當(dāng)前聊天室共有"+clients.size()+"人"); }}}}private void sendUserLogin(String msg) {for(Map.Entry<String,Socket> entry:clients.entrySet()) {Socket socket=entry.getValue();try {PrintStream out=new PrintStream(socket.getOutputStream(),true,"UTF-8");out.println(msg);}catch (IOException e) {e.printStackTrace();}}}}好友列表和群聊列表模塊
用畫布畫出好友列表和群聊列表窗口的靜態(tài)部分,動態(tài)部分由代碼實現(xiàn)
分析
登陸成功后程序邏輯
服務(wù)端
1.保存新用戶上線信息??request
2.將當(dāng)前所有在線用戶發(fā)回給新用戶?response
3.將新用戶上線信息發(fā)給所有其他用戶(上線提醒)
客戶端
1.與服務(wù)器建立連接,將自己的用戶名與Socket保存到服務(wù)端緩存
2.讀取服務(wù)端的所有在線好友信息
3.新建一個后臺線程不斷讀取服務(wù)器發(fā)來的信息
?
?
public class FriendsList {private JButton createGroupBtn;private JScrollPane friendsList;private JScrollPane groupListPanel;private JPanel friendsPanel;private JFrame frame;private String userName;private Set<String> users;private Connect2Server connect2Server;public FriendsList(String userName, Set<String> users, Connect2Server connect2Server) {this.userName = userName;this.users = users;this.connect2Server = connect2Server;frame = new JFrame(userName);frame.setContentPane(friendsPanel);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setSize(400, 300);//使窗口顯示在正中央frame.setLocationRelativeTo(null);//圖形界面可視, 默認(rèn)不可視frame.setVisible(true);//加載所有在線用戶信息loadUsers();//啟動后臺線程不斷監(jiān)聽服務(wù)器發(fā)來的消息*************************************Thread daemonThread = new Thread(new DaemonTask());//設(shè)置為守護線程daemonThread.setDaemon(true);daemonThread.start();}//好友列表后臺任務(wù),不斷監(jiān)聽服務(wù)器發(fā)來的信息,好友上限提醒,用戶私聊,群聊private class DaemonTask implements Runnable {private Scanner in = new Scanner(connect2Server.getIn());@Overridepublic void run() {while (true) {if (in.hasNextLine()) {//接收服務(wù)到發(fā)來的信息String strFromServer = in.nextLine();//此時服務(wù)器端發(fā)來一個Json字串if (strFromServer.startsWith("{")) {} else {if (strFromServer.startsWith("newLogin:")) { //好友上線邏輯String newFriendName = strFromServer.split(":")[1];users.add(newFriendName);JOptionPane.showMessageDialog(frame,newFriendName + "上線了!", "上線提醒", JOptionPane.INFORMATION_MESSAGE);//刷新好友列表loadUsers();}}}}}}private void loadUsers() {//創(chuàng)建好友列表的盤子JLabel[] userLables = new JLabel[users.size()];JPanel friends = new JPanel();//垂直布局friends.setLayout(new BoxLayout(friends, BoxLayout.Y_AXIS));Iterator<String> iterator = users.iterator();int i = 0;while (iterator.hasNext()) {String userName = iterator.next();userLables[i] = new JLabel(userName); //將好友列表加到盤子中friends.add(userLables[i]);i++;}//將所有的好友的數(shù)組盤子加到friendsList的大盤子friendsList.setViewportView(friends);//設(shè)置滾動條垂直滾動 friendsList.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);//刷新friends.revalidate();friendsList.revalidate();} }用戶私聊邏輯
用畫布畫出該窗口的靜態(tài)部分,顯示聊天信息部分由代碼實現(xiàn)
?
分析
客戶端:
主動發(fā)送方
1.點擊要私聊的用戶標(biāo)簽,彈出私聊界面 (緩存私聊界面對象)?
2.按照指定的協(xié)議向服務(wù)器發(fā)送私聊信息
? ? ? ?// type:2
? ? ? // content:sender-msg
? ? ? // to:目標(biāo)客戶端的用戶名
package com.lin.client.service;import com.lin.util.CommUtils; import com.lin.vo.MessageVO;import javax.swing.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.PrintStream; import java.io.UnsupportedEncodingException;/*** Description:* Author: llf* Created in 2019/8/24 18:55*/ public class PrivateChatGUI {private JTextArea readFromServer;private JPanel privateChatPanel;private JTextField send2Server;private String friendName;private String myName;private Connect2Server connect2Server;private JFrame frame;private PrintStream out;public PrivateChatGUI(String friendName,String myName,Connect2Server connect2Server) {this.friendName=friendName;this.connect2Server=connect2Server;this.myName=myName;try {this.out=new PrintStream(connect2Server.getOut(),true,"UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}frame = new JFrame("與"+friendName+"私聊中...");frame.setContentPane(privateChatPanel);//設(shè)置窗口關(guān)閉操作,將其設(shè)置為隱藏frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);frame.setSize(400,400);frame.setVisible(true);//捕捉輸入框的鍵盤輸入send2Server.addKeyListener(new KeyAdapter() {@Overridepublic void keyPressed(KeyEvent e) {StringBuilder sb=new StringBuilder();sb.append(send2Server.getText());//當(dāng)捕捉到按下enterif(e.getKeyCode()==KeyEvent.VK_ENTER) {String msg=sb.toString();MessageVO messageVO=new MessageVO();messageVO.setType("2");messageVO.setContent(myName+"-"+msg);messageVO.setTo(friendName);PrivateChatGUI.this.out.println(CommUtils.object2Json(messageVO));//將自己發(fā)送的信息展示到當(dāng)前私聊界面*******readFromServer(myName+"說:"+ msg);send2Server.setText("");}}});}//給信息接收者public void readFromServer(String s) {readFromServer.append(s+"\n");}public JFrame getFreame() {return this.frame;} }?
服務(wù)端:
1.收到客戶端發(fā)來的私聊信息,取出目標(biāo)端Socket,做信息轉(zhuǎn)發(fā)?
? ? ?// type:2
? ? ?// content:sender-msg
? ? ?// to
private static class ExecuteClient implements Runnable{private Socket client;private Scanner in;private PrintStream out;public ExecuteClient(Socket client) {this.client=client;try {this.in=new Scanner(client.getInputStream());this.out=new PrintStream(client.getOutputStream(),true,"UTF-8");} catch (IOException e) {e.printStackTrace();}}@Overridepublic void run() {while (true) {if (in.hasNextLine()) {String jsonStrFromClient = in.nextLine();MessageVO msgFromClient = (MessageVO) CommUtils.json2Object(jsonStrFromClient, MessageVO.class);if (msgFromClient.getType().equals("1")) {//用戶登錄邏輯}else if (msgFromClient.getType().equals("2")) {//用戶私聊 Type2//Content:myName-msg//to:friendNameString friendName=msgFromClient.getTo();Socket clientSocket=clients.get(friendName);try{PrintStream out=new PrintStream(clientSocket.getOutputStream(),true,"UTF-8");MessageVO msg2Client=new MessageVO();msg2Client.setType("2");msg2Client.setContent(msgFromClient.getContent());System.out.println("收到私聊消息,內(nèi)容為:"+msgFromClient.getContent());out.println(CommUtils.object2Json(msg2Client));}catch (IOException ex){ex.printStackTrace();} }}}}信息接收方:
1.判斷服務(wù)端發(fā)來的消息是否是私聊信息,若是,判斷緩存中是否有該界面?
2.按照指定協(xié)議來讀取內(nèi)容,發(fā)送消息
//緩存所有私聊界面private Map<String, PrivateChatGUI> privateChatGUIList = new ConcurrentHashMap<>();private class DaemonTask implements Runnable {private Scanner in = new Scanner(connect2Server.getIn());@Overridepublic void run() {while (true) {if (in.hasNextLine()) {//接收服務(wù)到發(fā)來的信息String strFromServer = in.nextLine();//此時服務(wù)器端發(fā)來一個Json字串if (strFromServer.startsWith("{")) {MessageVO messageVO = (MessageVO) CommUtils.json2Object(strFromServer, MessageVO.class);if (messageVO.getType().equals("2")) {String friendName = messageVO.getContent().split("-")[0];String msg = messageVO.getContent().split("-")[1];//判斷此私聊是不是第一次創(chuàng)建if (privateChatGUIList.containsKey(friendName)) {PrivateChatGUI privateChatGUI = privateChatGUIList.get(friendName);privateChatGUI.getFreame().setVisible(true);privateChatGUI.readFromServer(friendName + "說:" + msg);} else {PrivateChatGUI privateChatGUI = new PrivateChatGUI(friendName, userName, connect2Server);privateChatGUIList.put(friendName, privateChatGUI);privateChatGUI.readFromServer(friendName + "說:" + msg);}}} else {if (strFromServer.startsWith("newLogin:")) { //********************** 好友上線邏輯String newFriendName = strFromServer.split(":")[1];users.add(newFriendName);JOptionPane.showMessageDialog(frame,newFriendName + "上線了!", "上線提醒", JOptionPane.INFORMATION_MESSAGE);//刷新好友列表loadUsers();}}}}}}群聊創(chuàng)建
用畫布畫出該窗口的靜態(tài)部分,顯示好友列表的復(fù)選框部分由代碼實現(xiàn)
分析:點擊提交信息,獲取表單參數(shù),發(fā)送到服務(wù)端
客戶端
? ? ?// type:3
? ? ?// content:groupName
? ? ?// to:selectedFriends
package com.lin.client.service;import com.lin.util.CommUtils; import com.lin.vo.MessageVO; import javafx.scene.effect.SepiaTone; import sun.plugin2.message.Message;import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.util.HashSet; import java.util.Iterator; import java.util.Set;/*** Description:* Author: llf* Created in 2019/8/29 18:43*/ public class CreateGroupGUI {private JPanel createGroupPanel;private JTextField groupNameText;private JButton conformBtn;private JPanel friendLabelPanel;private String myName;private Set<String> friends;private Connect2Server connect2Server;private FriendsList friendsList;public CreateGroupGUI(String myName,Set<String> friends,Connect2Server connect2Server,FriendsList friendsList){this.myName=myName;this.friends=friends;this.connect2Server=connect2Server;this.friendsList=friendsList;JFrame frame = new JFrame("創(chuàng)建群聊");frame.setContentPane(createGroupPanel);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setSize(400,300);//居中frame.setLocationRelativeTo(null);frame.setVisible(true);//將在線好友以checkBox展示到界面中friendLabelPanel.setLayout(new BoxLayout(friendLabelPanel,BoxLayout.Y_AXIS));Iterator<String> iterator=friends.iterator();while (iterator.hasNext()) {String lableName=iterator.next();JCheckBox checkBox=new JCheckBox(lableName);friendLabelPanel.add(checkBox);}friendLabelPanel.revalidate();conformBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {Set<String> selectedFriends=new HashSet<>();Component[] comps=friendLabelPanel.getComponents();for (Component comp : comps) {//向下轉(zhuǎn)型JCheckBox checkBox= (JCheckBox) comp;if (checkBox.isSelected()) {String labelName= checkBox.getText();selectedFriends.add(labelName);}}//創(chuàng)建記得加入自己的名字selectedFriends.add(myName);//獲取群名String groupName=groupNameText.getText();//發(fā)送給服務(wù)器MessageVO messageVO=new MessageVO();/*** 協(xié)議3*/messageVO.setType("3");messageVO.setContent(groupName);messageVO.setTo(CommUtils.object2Json(selectedFriends));try {PrintStream out=new PrintStream(connect2Server.getOut(),true,"UTF-8");out.println(CommUtils.object2Json(messageVO));} catch (UnsupportedEncodingException e1) {e1.printStackTrace();}//將當(dāng)前群聊界面隱藏frame.setVisible(false);friendsList.addGroup(groupName,selectedFriends);friendsList.loadGroupList();}});}}服務(wù)端
//緩存當(dāng)前服務(wù)器所有的群名以及群好友private static Map<String,Set<String>> groups=new ConcurrentHashMap<>(); private static class ExecuteClient implements Runnable{private Socket client;private Scanner in;private PrintStream out;public ExecuteClient(Socket client) {this.client=client;try {this.in=new Scanner(client.getInputStream());this.out=new PrintStream(client.getOutputStream(),true,"UTF-8");} catch (IOException e) {e.printStackTrace();}}@Overridepublic void run() {while (true) {if (in.hasNextLine()) {String jsonStrFromClient = in.nextLine();MessageVO msgFromClient = (MessageVO) CommUtils.json2Object(jsonStrFromClient, MessageVO.class);if (msgFromClient.getType().equals("1")) {//用戶登陸邏輯}else if (msgFromClient.getType().equals("2")) {//用戶私聊邏輯}else if (msgFromClient.getType().equals("3")) {String groupName=msgFromClient.getContent();Set<String> friends= (Set<String>) CommUtils.json2Object(msgFromClient.getTo(), Set.class);groups.put(groupName,friends);System.out.println("有新的群注冊成功,群名為:"+groupName+",一共有"+groups.size()+"個群");} }}}好友列表窗口
//存儲所有的群名稱和群好友private Map<String, Set<String>> groupMap = new ConcurrentHashMap<>(); public void addGroup(String groupName, Set<String> friends) {groupMap.put(groupName, friends);}public void loadGroupList() {//存儲所有群名稱的標(biāo)簽JpanelJPanel groupNamePanel = new JPanel();groupNamePanel.setLayout(new BoxLayout(groupNamePanel, BoxLayout.Y_AXIS));JLabel[] labels = new JLabel[groupMap.size()];//Map遍歷Set<String> entries = groupMap.keySet();Iterator<String> iterator = entries.iterator();int i = 0;while (iterator.hasNext()) {String groupName = iterator.next();labels[i] = new JLabel(groupName);//添加到自定義的盤子groupNamePanel.add(labels[i]);i++;}//將自定義盤子加到滾動條中g(shù)roupListPanel.setViewportView(groupNamePanel);//設(shè)置垂直滾動groupListPanel.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);//刷新groupListPanel.revalidate();}群聊消息
用畫布畫出該窗口的靜態(tài)部分,顯示好友列表和群聊消息的部分由代碼實現(xiàn)
?
//緩存所有的群聊頁面 群聊名 和群聊界面private Map<String, GroupChatGUI> groupChatGUIMap=new ConcurrentHashMap<>();//群聊點擊事件private class GroupLabelAction implements MouseListener {private String groupName;public GroupLabelAction(String groupName) {this.groupName=groupName;}@Overridepublic void mouseClicked(MouseEvent e) {if (groupChatGUIMap.containsKey(groupName)) {GroupChatGUI groupChatGUI=groupChatGUIMap.get(groupName);groupChatGUI.getFrame().setVisible(true);}else {// 獲得所有的群聊成員Set<String> names=groupMap.get(groupName);GroupChatGUI groupChatGUI=new GroupChatGUI(groupName,names,userName,connect2Server);groupChatGUIMap.put(groupName,groupChatGUI);}}@Overridepublic void mousePressed(MouseEvent e) {}@Overridepublic void mouseReleased(MouseEvent e) {}@Overridepublic void mouseEntered(MouseEvent e) {}@Overridepublic void mouseExited(MouseEvent e) {}}分析
客戶端
群聊發(fā)起者?
1.點擊群名稱標(biāo)簽,彈出群聊界面
2.將群聊內(nèi)容發(fā)送到服務(wù)端
//?type:4
//?content:senderName-msg
//?to:groupName
服務(wù)端?
1.收到群聊信息,解出群名
2.根據(jù)群名稱找到該群的所有用戶的用戶名
3.再根據(jù)用戶名找到該用戶的Socket,發(fā)送群聊信息
//?type:4
//?content:senderName-msg
//?to:groupName-[該群的所有群成員]
?
@Overridepublic void run() {while (true) {if (in.hasNextLine()) {String jsonStrFromClient = in.nextLine();MessageVO msgFromClient = (MessageVO) CommUtils.json2Object(jsonStrFromClient, MessageVO.class);if (msgFromClient.getType().equals("1")) {//登陸成功邏輯}else if (msgFromClient.getType().equals("2")) {//用戶私聊 }else if (msgFromClient.getType().equals("3")) {//創(chuàng)建群組}else if (msgFromClient.getType().equals("4")) {// type:4// content:myName-msg// to:groupNameString groupName=msgFromClient.getTo();Set<String> names=groups.get(groupName);Iterator<String> iterator=names.iterator();while (iterator.hasNext()) {String socketName=iterator.next();Socket client=clients.get(socketName);try {PrintStream out=new PrintStream(client.getOutputStream(),true,"UTF-8");/*** typr=4* content直接轉(zhuǎn)發(fā)* 組名-所有的群成員*/MessageVO messageVO=new MessageVO();messageVO.setType("4");messageVO.setContent(msgFromClient.getContent());messageVO.setTo(groupName+"-"+CommUtils.object2Json(names));out.println(CommUtils.object2Json(messageVO));System.out.println("服務(wù)器發(fā)送的群聊消息為:"+messageVO);} catch (IOException e) {e.printStackTrace();}}}}}群聊接收者
1.當(dāng)?shù)谝淮问盏饺盒畔?#xff0c;先刷新本客戶端的群聊列表信息
2.彈出群聊界面
?
?
總結(jié)
以上是生活随笔為你收集整理的JavaGUI--模拟QQ聊天界面私聊群聊的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Joomla 插件 attachment
- 下一篇: Java LeetCode每日一题-从易