您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)怎么在Java中利用GUI編程實(shí)現(xiàn)在線聊天室,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
客戶端的功能主要包括如下的功能:
選擇連上服務(wù)端
顯示當(dāng)前房間列表(包括房間號(hào)和房間名稱)
選擇房間進(jìn)入
多個(gè)用戶在線群聊
可以發(fā)送表情(用本地的,實(shí)際上發(fā)送只發(fā)送表情的代碼)
退出房間
選擇創(chuàng)建房間
房間里沒人(房主退出),導(dǎo)致房間解散
顯示系統(tǒng)提示消息
顯示用戶消息
構(gòu)造標(biāo)準(zhǔn)的消息結(jié)構(gòu)發(fā)送
維護(hù)GUI所需的數(shù)據(jù)模型
服務(wù)端的功能主要包括:
維護(hù)用戶信息和房間信息
處理用戶發(fā)送來的消息選擇轉(zhuǎn)發(fā)或者回復(fù)處理結(jié)果
構(gòu)造標(biāo)準(zhǔn)的消息結(jié)構(gòu)發(fā)送
架構(gòu)
整個(gè)程序采用C/S設(shè)計(jì)架構(gòu),分為一個(gè)服務(wù)端和多個(gè)客戶端。服務(wù)端開放一個(gè)端口給所有開客戶端,客戶端連接該端口并收發(fā)信息,服務(wù)端在內(nèi)部維護(hù)客戶端的組,并對(duì)每一個(gè)客戶端都用一個(gè)子線程來收發(fā)信息
基本類的設(shè)計(jì)
User類
package User; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; /** * * @author lannooo * */ public class User { private String name; private long id; private long roomId; private Socket socket; private BufferedReader br; private PrintWriter pw; /** * * @param name: 設(shè)置user的姓名 * @param id:設(shè)置user的id * @param socket:保存用戶連接的socket * @throws IOException */ public User(String name, long id, final Socket socket) throws IOException { this.name=name; this.id=id; this.socket=socket; this.br=new BufferedReader(new InputStreamReader( socket.getInputStream())); this.pw=new PrintWriter(socket.getOutputStream()); } /** * 獲得該用戶的id * @return id */ public long getId() { return id; } /** * 設(shè)置該用戶的id * @param id 新的id */ public void setId(long id) { this.id = id; } /** * 獲得用戶當(dāng)前所在的房間號(hào) * @return roomId */ public long getRoomId() { return roomId; } /** * 設(shè)置當(dāng)前用戶的所在的房間號(hào) * @param roomId */ public void setRoomId(long roomId) { this.roomId = roomId; } /** * 設(shè)置當(dāng)前用戶在聊天室中的昵稱 * @param name */ public void setName(String name) { this.name = name; } /** * 返回當(dāng)前用戶在房間中的昵稱 * @return */ public String getName() { return name; } /** * 返回當(dāng)前用戶連接的socket實(shí)例 * @return */ public Socket getSocket() { return socket; } /** * 設(shè)置當(dāng)前用戶連接的socket * @param socket */ public void setSocket(Socket socket) { this.socket = socket; } /** * 獲得該用戶的消息讀取輔助類BufferedReader實(shí)例 * @return */ public BufferedReader getBr() { return br; } /** * 設(shè)置 用戶的消息讀取輔助類 * @param br */ public void setBr(BufferedReader br) { this.br = br; } /** * 獲得消息寫入類實(shí)例 * @return */ public PrintWriter getPw() { return pw; } /** * 設(shè)置消息寫入類實(shí)例 * @param pw */ public void setPw(PrintWriter pw) { this.pw = pw; } /** * 重寫了用戶類打印的函數(shù) */ @Override public String toString() { return "#User"+id+"#"+name+"[#Room"+roomId+"#]<socket:"+socket+">"; } }
Room類
package Room; import java.util.ArrayList; import java.util.List; import User.User; /** * * @author lannooo * */ public class Room { private String name; private long roomId; private ArrayList<User> list; private int totalUsers; /** * 獲得房間的名字 * @return name */ public String getName() { return name; } /** * 設(shè)置房間的新名字 * @param name */ public void setName(String name) { this.name = name; } /** * 獲得房間的id號(hào) * @return */ public long getRoomId() { return roomId; } /** * 設(shè)置房間的id * @param roomId */ public void setRoomId(long roomId) { this.roomId = roomId; } /** * 向房間中加入一個(gè)新用戶 * @param user */ public void addUser(User user) { if(!list.contains(user)){ list.add(user); totalUsers++; }else{ System.out.println("User is already in Room<"+name+">:"+user); } } /** * 從房間中刪除一個(gè)用戶 * @param user * @return 目前該房間中的總用戶數(shù)目 */ public int delUser(User user){ if(list.contains(user)){ list.remove(user); return --totalUsers; }else{ System.out.println("User is not in Room<"+name+">:"+user); return totalUsers; } } /** * 獲得當(dāng)前房間的用戶列表 * @return */ public ArrayList<User> getUsers(){ return list; } /** * 獲得當(dāng)前房間的用戶昵稱的列表 * @return */ public String[] getUserNames(){ String[] userList = new String[list.size()]; int i=0; for(User each: list){ userList[i++]=each.getName(); } return userList; } /** * 使用房間的名稱和id來new一個(gè)房間 * @param name * @param roomId */ public Room(String name, long roomId) { this.name=name; this.roomId=roomId; this.totalUsers=0; list = new ArrayList<>(); } }
RoomList類
package Room; import java.awt.image.DirectColorModel; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import User.User; /** * * @author lannooo * */ public class RoomList { private HashMap<Long, Room> map; private long unusedRoomId; public static long MAX_ROOMS = 9999; private int totalRooms; /** * 未使用的roomid從1算起,起始的房間總數(shù)為0 */ public RoomList(){ map = new HashMap<>(); unusedRoomId = 1; totalRooms = 0; } /** * 創(chuàng)建一個(gè)新的房間,使用未使用的房間號(hào)進(jìn)行創(chuàng)建,如果沒有可以使用的則就創(chuàng)建失敗 * @param name: 房間的名字 * @return 創(chuàng)建的房間的id */ public long createRoom(String name){ if(totalRooms<MAX_ROOMS){ if(name.length()==0){ name = ""+unusedRoomId; } Room room = new Room(name, unusedRoomId); map.put(unusedRoomId, room); totalRooms++; return unusedRoomId++; }else{ return -1; } } /** * 用戶加入一個(gè)房間 * @param user * @param roomID * @return */ public boolean join(User user, long roomID){ if(map.containsKey(roomID)){ map.get(roomID).addUser(user); return true; }else{ return false; } } /** * 用戶退出他的房間 * @param user * @param roomID * @return */ public int esc(User user, long roomID){ if(map.containsKey(roomID)){ int number = map.get(roomID).delUser(user); /*如果這個(gè)房間剩下的人數(shù)為0,那么刪除該房間*/ if(number==0){ map.remove(roomID); totalRooms--; return 0; } return 1; }else{ return -1; } } /** * 列出所有房間的列表,返回一個(gè)二維數(shù)組,strings[i][0]放房間的id,string[i][1]放房間的name * @return */ public String[][] listRooms(){ String[][] strings = new String[totalRooms][2]; int i=0; /*將map轉(zhuǎn)化為set并使用迭代器來遍歷*/ Set<Entry<Long, Room>> set = map.entrySet(); Iterator<Entry<Long, Room>> iterator = set.iterator(); while(iterator.hasNext()){ Map.Entry<Long, Room> entry = iterator.next(); long key = entry.getKey(); Room value = entry.getValue(); strings[i][0]=""+key; strings[i][1]=value.getName(); } return strings; } /** * 通過roomID來獲得房間 * @param roomID * @return */ public Room getRoom(long roomID){ if(map.containsKey(roomID)){ return map.get(roomID); } else return null; } }
服務(wù)端
Server
package Server; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.json.*; import Room.Room; import Room.RoomList; import User.User; /** * * @author lannooo * */ public class Server { private ArrayList<User> allUsers; private RoomList rooms; private int port; private ServerSocket ss; private long unusedUserID; public final long MAX_USERS = 999999; /** * 通過port號(hào)來構(gòu)造服務(wù)器端對(duì)象 * 維護(hù)一個(gè)總的用戶列表和一個(gè)房間列表 * @param port * @throws Exception */ public Server(int port) throws Exception { allUsers = new ArrayList<>(); rooms = new RoomList(); this.port=port; unusedUserID=1; ss = new ServerSocket(port); System.out.println("Server is builded!"); } /** * 獲得下一個(gè)可用的用戶id * @return */ private long getNextUserID(){ if(unusedUserID < MAX_USERS) return unusedUserID++; else return -1; } /** * 開始監(jiān)聽,當(dāng)接受到新的用戶連接,就創(chuàng)建一個(gè)新的用戶,并添加到用戶列表中 * 然后創(chuàng)建一個(gè)新的服務(wù)線程用于收發(fā)該用戶的消息 * @throws Exception */ public void startListen() throws Exception{ while(true){ Socket socket = ss.accept(); long id = getNextUserID(); if(id != -1){ User user = new User("User"+id, id, socket); System.out.println(user.getName() + " is login..."); allUsers.add(user); ServerThread thread = new ServerThread(user, allUsers, rooms); thread.start(); }else{ System.out.println("Server is full!"); socket.close(); } } } /** * 測(cè)試用main方法,設(shè)置偵聽端口為9999,并開始監(jiān)聽 * @param args */ public static void main(String[] args) { try { Server server = new Server(9999); server.startListen(); } catch (Exception e) { e.printStackTrace(); } } }
ServerThread
package Server; import java.io.IOException; import java.io.PrintWriter; import java.net.SocketException; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import Room.Room; import Room.RoomList; import User.User; /** * * @author lannooo * */ public class ServerThread extends Thread { private User user; private ArrayList<User> userList;/*保存用戶列表*/ private RoomList map; /*保存房間列表*/ private long roomId; private PrintWriter pw; /** * 通過用戶的對(duì)象實(shí)例、全局的用戶列表、房間列表進(jìn)行構(gòu)造 * @param user * @param userList * @param map */ public ServerThread(User user, ArrayList<User> userList, RoomList map){ this.user=user; this.userList=userList; this.map=map; pw=null; roomId = -1; } /** * 線程運(yùn)行部分,持續(xù)讀取用戶socket發(fā)送來的數(shù)據(jù),并解析 */ public void run(){ try{ while (true) { String msg=user.getBr().readLine(); System.out.println(msg); /*解析用戶的數(shù)據(jù)格式*/ parseMsg(msg); } }catch (SocketException se) { /*處理用戶斷開的異常*/ System.out.println("user "+user.getName()+" logout."); }catch (Exception e) { /*處理其他異常*/ e.printStackTrace(); }finally { try { /* * 用戶斷開或者退出,需要把該用戶移除 * 并關(guān)閉socket */ remove(user); user.getBr().close(); user.getSocket().close(); } catch (IOException ioe) { ioe.printStackTrace(); } } } /** * 用正則表達(dá)式匹配數(shù)據(jù)的格式,根據(jù)不同的指令類型,來調(diào)用相應(yīng)的方法處理 * @param msg */ private void parseMsg(String msg){ String code = null; String message=null; if(msg.length()>0){ /*匹配指令類型部分的字符串*/ Pattern pattern = Pattern.compile("<code>(.*)</code>"); Matcher matcher = pattern.matcher(msg); if(matcher.find()){ code = matcher.group(1); } /*匹配消息部分的字符串*/ pattern = Pattern.compile("<msg>(.*)</msg>"); matcher = pattern.matcher(msg); if(matcher.find()){ message = matcher.group(1); } switch (code) { case "join": // add to the room // code = 1, 直接顯示在textArea中 // code = 11, 在list中加入 // code = 21, 把當(dāng)前房間里的所有用戶返回給client if(roomId == -1){ roomId = Long.parseLong(message); map.join(user, roomId); sendRoomMsgExceptSelf(buildCodeWithMsg("<name>"+user.getName()+"</name><id>"+user.getId()+"</id>", 11)); // 這個(gè)消息需要加入房間里已有用戶的列表 returnMsg(buildCodeWithMsg("你加入了房間:" + map.getRoom(roomId).getName(), 1)); returnMsg(buildCodeWithMsg(getMembersInRoom(), 21)); }else{ map.esc(user, roomId); sendRoomMsg(buildCodeWithMsg(""+user.getId(), 12)); long oldRoomId = roomId; roomId = Long.parseLong(message); map.join(user, roomId); sendRoomMsgExceptSelf(buildCodeWithMsg("<name>"+user.getName()+"</name><id>"+user.getId()+"</id>", 11)); returnMsg(buildCodeWithMsg("你退出房間:" + map.getRoom(oldRoomId).getName() + ",并加入了房間:" + roomId,1)); returnMsg(buildCodeWithMsg(getMembersInRoom(), 21)); } break; case "esc": // delete from room list // code = 2, 彈窗提示 // code = 12, 對(duì)所有該房間的其他用戶發(fā)送該用戶退出房間的信息,從list中刪除 if(roomId!=-1){ int flag=map.esc(user, roomId); sendRoomMsgExceptSelf(buildCodeWithMsg(""+user.getId(), 12)); long oldRoomId=roomId; roomId = -1; returnMsg(buildCodeWithMsg("你已經(jīng)成功退出房間,不會(huì)收到消息", 2)); if(flag==0){ sendMsg(buildCodeWithMsg(""+oldRoomId, 13)); } }else{ returnMsg(buildCodeWithMsg("你尚未加入任何房間", 2)); } break; case "list": // list all the rooms // code = 3, 在客戶端解析rooms,并填充roomlist returnMsg(buildCodeWithMsg(getRoomsList(), 3)); break; case "message": // send message // code = 4, 自己收到的話,打印的是‘你說:....'否則打印user id對(duì)應(yīng)的name sendRoomMsg(buildCodeWithMsg("<from>"+user.getId()+"</from><smsg>"+message+"</smsg>", 4)); break; case "create": // create a room // code=5,提示用戶進(jìn)入了房間 // code=15,需要在其他所有用戶的room列表中更新 roomId = map.createRoom(message); map.join(user, roomId); sendMsg(buildCodeWithMsg("<rid>"+roomId+"</rid><rname>"+message+"</rname>", 15)); returnMsg(buildCodeWithMsg("你進(jìn)入了創(chuàng)建的房間:"+map.getRoom(roomId).getName(), 5)); returnMsg(buildCodeWithMsg(getMembersInRoom(), 21)); break; case "setname": // set name for user // code=16,告訴房間里的其他人,你改了昵稱 user.setName(message); sendRoomMsg(buildCodeWithMsg("<id>"+user.getId()+"</id><name>"+message+"</name>", 16)); break; default: // returnMsg("something unknown"); System.out.println("not valid message from user"+user.getId()); break; } } } /** * 獲得該用戶房間中的所有用戶列表,并構(gòu)造成一定格式的消息返回 * @return */ private String getMembersInRoom(){ /*先從room列表獲得該用戶的room*/ Room room = map.getRoom(roomId); StringBuffer stringBuffer = new StringBuffer(); if(room != null){ /*獲得房間中所有的用戶的列表,然后構(gòu)造成一定的格式發(fā)送回去*/ ArrayList<User> users = room.getUsers(); for(User each: users){ stringBuffer.append("<member><name>"+each.getName()+ "</name><id>"+each.getId()+"</id></member>"); } } return stringBuffer.toString(); } /** * 獲得所有房間的列表,并構(gòu)造成一定的格式 * @return */ private String getRoomsList(){ String[][] strings = map.listRooms(); StringBuffer sb = new StringBuffer(); for(int i=0; i<strings.length; i++){ sb.append("<room><rname>"+strings[i][1]+ "</rname><rid>"+strings[i][0]+"</rid></room>"); } return sb.toString(); } /** * 構(gòu)造成一個(gè)統(tǒng)一的消息格式 * @param msg * @param code * @return */ private String buildCodeWithMsg(String msg, int code){ return "<code>"+code+"</code><msg>"+msg+"</msg>\n"; } /** * 這個(gè)是群發(fā)消息:全體用戶,code>10 * @param msg */ private void sendMsg(String msg) { // System.out.println("In sendMsg()"); /*取出用戶列表中的每一個(gè)用戶來發(fā)送消息*/ for(User each:userList){ try { pw=each.getPw(); pw.println(msg); pw.flush(); System.out.println(msg); } catch (Exception e) { System.out.println("exception in sendMsg()"); } } } /** * 只對(duì)同一房間的用戶發(fā):code>10 * @param msg */ private void sendRoomMsg(String msg){ /*先獲得該用戶的房間號(hào),然后往該房間發(fā)送消息*/ Room room = map.getRoom(roomId); if(room != null){ ArrayList<User> users = room.getUsers(); for(User each: users){ pw = each.getPw(); pw.println(msg); pw.flush(); } } } /** * 向房間中除了該用戶自己,發(fā)送消息 * @param msg */ private void sendRoomMsgExceptSelf(String msg){ Room room = map.getRoom(roomId); if(room != null){ ArrayList<User> users = room.getUsers(); for(User each: users){ if(each.getId()!=user.getId()){ pw = each.getPw(); pw.println(msg); pw.flush(); } } } } /** * 對(duì)于client的來信,返回一個(gè)結(jié)果,code<10 * @param msg */ private void returnMsg(String msg){ try{ pw = user.getPw(); pw.println(msg); pw.flush(); }catch (Exception e) { System.out.println("exception in returnMsg()"); } } /** * 移除該用戶,并向房間中其他用戶發(fā)送該用戶已經(jīng)退出的消息 * 如果房間中沒人了,那么就更新房間列表給所有用戶 * @param user */ private void remove(User user){ if(roomId!=-1){ int flag=map.esc(user, roomId); sendRoomMsgExceptSelf(buildCodeWithMsg(""+user.getId(), 12)); long oldRoomId=roomId; roomId = -1; if(flag==0){ sendMsg(buildCodeWithMsg(""+oldRoomId, 13)); } } userList.remove(user); } }
客戶端
Client
package Client; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.HashMap; import java.util.Iterator; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.DefaultListModel; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.JTextPane; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.text.BadLocationException; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.Style; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; /** * * @author lannooo * */ public class Client implements ActionListener{ private JFrame frame; private Socket socket; private BufferedReader br; private PrintWriter pw; private String name; private HashMap<String, Integer> rooms_map; private HashMap<String, Integer> users_map; private JTextField host_textfield; private JTextField port_textfield; private JTextField text_field; private JTextField name_textfiled; private JLabel rooms_label; private JLabel users_label; private JList<String> roomlist; private JList<String> userlist; private JTextPane msgArea; private JScrollPane textScrollPane; private JScrollBar vertical; DefaultListModel<String> rooms_model; DefaultListModel<String> users_model; /* * 構(gòu)造函數(shù) * 該客戶端對(duì)象維護(hù)兩個(gè)map,房間的hashmap和房間中用戶的hashmap * 作為列表組件的數(shù)據(jù)模型 */ public Client(){ rooms_map = new HashMap<>(); users_map = new HashMap<>(); initialize(); } /** * 連接服務(wù)端,指定host和port * @param host * @param port * @return */ public boolean connect(String host, int port){ try { socket = new Socket(host, port); System.out.println("Connected to server!"+socket.getRemoteSocketAddress()); br=new BufferedReader(new InputStreamReader(System.in)); pw=new PrintWriter(socket.getOutputStream()); /* * 創(chuàng)建一個(gè)接受和解析服務(wù)器消息的線程 * 傳入當(dāng)前客戶端對(duì)象的指針,作為句柄調(diào)用相應(yīng)的處理函數(shù) */ ClientThread thread = new ClientThread(socket, this); thread.start(); return true; } catch (IOException e) { System.out.println("Server error"); JOptionPane.showMessageDialog(frame, "服務(wù)器無法連接!"); return false; } } /*當(dāng)前進(jìn)程作為只發(fā)送消息的線程,從命令行中獲取輸入*/ // public void sendMsg(){ // String msg; // try { // while(true){ // msg = br.readLine(); // pw.println(msg); // pw.flush(); // } // } catch (IOException e) { // System.out.println("error when read msg and to send."); // } // } /** * 發(fā)給服務(wù)器的消息,先經(jīng)過一定的格式構(gòu)造再發(fā)送 * @param msg * @param code */ public void sendMsg(String msg, String code){ try { pw.println("<code>"+code+"</code><msg>"+msg+"</msg>"); pw.flush(); } catch (Exception e) { //一般是沒有連接的問題 System.out.println("error in sendMsg()"); JOptionPane.showMessageDialog(frame, "請(qǐng)先連接服務(wù)器!"); } } /** * 窗口初始化 */ private void initialize() { /*設(shè)置窗口的UI風(fēng)格和字體*/ setUIStyle(); setUIFont(); JFrame frame = new JFrame("ChatOnline"); JPanel panel = new JPanel(); /*主要的panel,上層放置連接區(qū),下層放置消息區(qū), 中間是消息面板,左邊是room列表,右邊是當(dāng)前room的用戶列表*/ JPanel headpanel = new JPanel(); /*上層panel,用于放置連接區(qū)域相關(guān)的組件*/ JPanel footpanel = new JPanel(); /*下層panel,用于放置發(fā)送信息區(qū)域的組件*/ JPanel leftpanel = new JPanel(); /*左邊panel,用于放置房間列表和加入按鈕*/ JPanel rightpanel = new JPanel(); /*右邊panel,用于放置房間內(nèi)人的列表*/ /*最上層的布局,分中間,東南西北五個(gè)部分*/ BorderLayout layout = new BorderLayout(); /*格子布局,主要用來設(shè)置西、東、南三個(gè)部分的布局*/ GridBagLayout gridBagLayout = new GridBagLayout(); /*主要設(shè)置北部的布局*/ FlowLayout flowLayout = new FlowLayout(); /*設(shè)置初始窗口的一些性質(zhì)*/ frame.setBounds(100, 100, 800, 600); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setContentPane(panel); frame.setLayout(layout); /*設(shè)置各個(gè)部分的panel的布局和大小*/ headpanel.setLayout(flowLayout); footpanel.setLayout(gridBagLayout); leftpanel.setLayout(gridBagLayout); rightpanel.setLayout(gridBagLayout); leftpanel.setPreferredSize(new Dimension(130, 0)); rightpanel.setPreferredSize(new Dimension(130, 0)); /*以下均是headpanel中的組件*/ host_textfield = new JTextField("127.0.0.1"); port_textfield = new JTextField("9999"); name_textfiled = new JTextField("匿名"); host_textfield.setPreferredSize(new Dimension(100, 25)); port_textfield.setPreferredSize(new Dimension(70, 25)); name_textfiled.setPreferredSize(new Dimension(150, 25)); JLabel host_label = new JLabel("服務(wù)器IP"); JLabel port_label = new JLabel("端口"); JLabel name_label = new JLabel("昵稱"); JButton head_connect = new JButton("連接"); // JButton head_change = new JButton("確認(rèn)更改"); JButton head_create = new JButton("創(chuàng)建房間"); headpanel.add(host_label); headpanel.add(host_textfield); headpanel.add(port_label); headpanel.add(port_textfield); headpanel.add(head_connect); headpanel.add(name_label); headpanel.add(name_textfiled); // headpanel.add(head_change); headpanel.add(head_create); /*以下均是footpanel中的組件*/ JButton foot_emoji = new JButton("表情"); JButton foot_send = new JButton("發(fā)送"); text_field = new JTextField(); footpanel.add(text_field, new GridBagConstraints(0, 0, 1, 1, 100, 100, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); footpanel.add(foot_emoji, new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); footpanel.add(foot_send, new GridBagConstraints(2, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); /*兩邊的格子中的組件*/ rooms_label = new JLabel("當(dāng)前房間數(shù):0"); users_label = new JLabel("房間內(nèi)人數(shù):0"); JButton join_button = new JButton("加入房間"); JButton esc_button = new JButton("退出房間"); rooms_model = new DefaultListModel<>(); users_model = new DefaultListModel<>(); // rooms_model.addElement("房間1"); // rooms_model.addElement("房間2"); // rooms_model.addElement("房間3"); // String fangjian = "房間1"; // rooms_map.put(fangjian, 1); roomlist = new JList<>(rooms_model); userlist = new JList<>(users_model); JScrollPane roomListPane = new JScrollPane(roomlist); JScrollPane userListPane = new JScrollPane(userlist); leftpanel.add(rooms_label, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); leftpanel.add(join_button, new GridBagConstraints(0, 1, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); leftpanel.add(esc_button, new GridBagConstraints(0, 2, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); leftpanel.add(roomListPane, new GridBagConstraints(0, 3, 1, 1, 100, 100, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); rightpanel.add(users_label, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); rightpanel.add(userListPane,new GridBagConstraints(0, 1, 1, 1, 100, 100, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); /*中間的文本區(qū)組件*/ msgArea = new JTextPane(); msgArea.setEditable(false); textScrollPane = new JScrollPane(); textScrollPane.setViewportView(msgArea); vertical = new JScrollBar(JScrollBar.VERTICAL); vertical.setAutoscrolls(true); textScrollPane.setVerticalScrollBar(vertical); /*設(shè)置頂層布局*/ panel.add(headpanel, "North"); panel.add(footpanel, "South"); panel.add(leftpanel, "West"); panel.add(rightpanel, "East"); panel.add(textScrollPane, "Center"); /*注冊(cè)各種事件*/ /*連接服務(wù)器*/ head_connect.addActionListener(this); /*發(fā)送消息,如果沒有連接則會(huì)彈窗提示*/ foot_send.addActionListener(this); /*改名字*/ // head_change.addActionListener(this); /*創(chuàng)建房間*/ head_create.addActionListener(this); /*發(fā)送表情*/ foot_emoji.addActionListener(this); /*加入room*/ join_button.addActionListener(this); /*退出房間*/ esc_button.addActionListener(this); /*最終顯示*/ frame.setVisible(true); } /** * 事件監(jiān)聽處理 */ @Override public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); switch (cmd) { case "連接": /*點(diǎn)擊連接按鈕*/ String strhost = host_textfield.getText(); String strport = port_textfield.getText(); connect(strhost, Integer.parseInt(strport)); String nameSeted = JOptionPane.showInputDialog("請(qǐng)輸入你的昵稱:"); /*提示輸入昵稱*/ name_textfiled.setText(nameSeted); name_textfiled.setEditable(false); port_textfield.setEditable(false); host_textfield.setEditable(false); /*發(fā)送設(shè)置姓名的消息和列出用戶列表的消息*/ sendMsg(nameSeted, "setname"); sendMsg("", "list"); break; // case "確認(rèn)更改": // String strname = name_textfiled.getText(); // name = strname; // sendMsg(strname, "setname"); // break; case "加入房間": /*選擇房間后,點(diǎn)擊加入房間按鈕*/ String selected = roomlist.getSelectedValue(); if(rooms_map.containsKey(selected)){ sendMsg(""+rooms_map.get(selected), "join"); } break; case "退出房間": /*點(diǎn)擊退出房間的按鈕*/ sendMsg("", "esc"); break; case "發(fā)送": /*點(diǎn)擊發(fā)送消息的按鈕*/ String text = text_field.getText(); text_field.setText(""); sendMsg(text, "message"); break; case "表情": /*發(fā)送表情,新建一個(gè)表情窗口,并直接在表情窗口中處理消息發(fā)送*/ IconDialog dialog = new IconDialog(frame, this); break; case "創(chuàng)建房間": /*點(diǎn)擊創(chuàng)建房間的按鈕,彈出提示框數(shù)據(jù)房間名稱*/ String string = JOptionPane.showInputDialog("請(qǐng)輸入你的房間名稱"); if(string==null || string.equals("")){ string = name+(int)(Math.random()*10000)+"的房間"; } sendMsg(string, "create"); break; default: break; } } /*很多輔助和clientThread互動(dòng)的*/ /** * 加入用戶,通過正則表達(dá)式,匹配消息內(nèi)容中的用戶信息 * @param content */ public void addUser(String content){ if(content.length()>0){ Pattern pattern = Pattern.compile("<name>(.*)</name><id>(.*)</id>"); Matcher matcher = pattern.matcher(content); if(matcher.find()){ /* * 獲得用戶的name和id * 加入用戶列表 * 在消息區(qū)顯示系統(tǒng)提示 */ String name = matcher.group(1); String id = matcher.group(2); insertUser(Integer.parseInt(id), name); insertMessage(textScrollPane, msgArea, null, "系統(tǒng):", name+"加入了聊天室"); } } users_label.setText("房間內(nèi)人數(shù):"+users_map.size()); /*更新房間內(nèi)的人數(shù)*/ } /** * 刪除用戶 * @param content */ public void delUser(String content){ if(content.length()>0){ int id = Integer.parseInt(content); /* * 從維護(hù)的用戶map中取得所有的用戶名字,然后去遍歷匹配的用戶 * 匹配到的用戶名字從相應(yīng)的數(shù)據(jù)模型中移除 * 并從map中移除,并在消息框中提示系統(tǒng)消息 */ Set<String> set = users_map.keySet(); Iterator<String> iter = set.iterator(); String name=null; while(iter.hasNext()){ name = iter.next(); if(users_map.get(name)==id){ users_model.removeElement(name); break; } } users_map.remove(name); insertMessage(textScrollPane, msgArea, null, "系統(tǒng):", name+"退出了聊天室"); } users_label.setText("房間內(nèi)人數(shù):"+users_map.size()); } /** * 更新用戶信息 * @param content */ public void updateUser(String content){ if(content.length()>0){ Pattern pattern = Pattern.compile("<id>(.*)</id><name>(.*)</name>"); Matcher matcher = pattern.matcher(content); if(matcher.find()){ String id = matcher.group(1); String name = matcher.group(2); insertUser(Integer.parseInt(id), name); } } } /** * 列出所有用戶 * @param content */ public void listUsers(String content){ String name = null; String id=null; Pattern rough_pattern=null; Matcher rough_matcher=null; Pattern detail_pattern=null; /* * 先用正則表達(dá)式匹配用戶信息 * 然后插入數(shù)據(jù)模型中 * 并更新用戶數(shù)據(jù)模型中的條目 */ if(content.length()>0){ rough_pattern = Pattern.compile("<member>(.*?)</member>"); rough_matcher = rough_pattern.matcher(content); while(rough_matcher.find()){ String detail = rough_matcher.group(1); detail_pattern = Pattern.compile("<name>(.*)</name><id>(.*)</id>"); Matcher detail_matcher = detail_pattern.matcher(detail); if(detail_matcher.find()){ name = detail_matcher.group(1); id = detail_matcher.group(2); insertUser(Integer.parseInt(id), name); } } } users_label.setText("房間內(nèi)人數(shù):"+users_map.size()); } /** * 直接在textarea中顯示消息 * @param content */ public void updateTextArea(String content){ insertMessage(textScrollPane, msgArea, null, "系統(tǒng):", content); } /** * 在textarea中顯示其他用戶的消息 * 先用正則匹配,再顯示消息 * 其中還需要匹配emoji表情的編號(hào) * @param content */ public void updateTextAreaFromUser(String content){ if(content.length()>0){ Pattern pattern = Pattern.compile("<from>(.*)</from><smsg>(.*)</smsg>"); Matcher matcher = pattern.matcher(content); if(matcher.find()){ String from = matcher.group(1); String smsg = matcher.group(2); String fromName = getUserName(from); if(fromName.equals(name)) fromName = "你"; if(smsg.startsWith("<emoji>")){ String emojiCode = smsg.substring(7, smsg.length()-8); // System.out.println(emojiCode); insertMessage(textScrollPane, msgArea, emojiCode, fromName+"說:", null); return ; } insertMessage(textScrollPane, msgArea, null, fromName+"說:", smsg); } } } /** * 顯示退出的結(jié)果 * @param content */ public void showEscDialog(String content){ JOptionPane.showMessageDialog(frame, content); /*清除消息區(qū)內(nèi)容,清除用戶數(shù)據(jù)模型內(nèi)容和用戶map內(nèi)容,更新房間內(nèi)人數(shù)*/ msgArea.setText(""); users_model.clear(); users_map.clear(); users_label.setText("房間內(nèi)人數(shù):0"); } /** * 新增一個(gè)room * @param content */ public void addRoom(String content){ if(content.length()>0){ Pattern pattern = Pattern.compile("<rid>(.*)</rid><rname>(.*)</rname>"); Matcher matcher = pattern.matcher(content); if(matcher.find()){ String rid = matcher.group(1); String rname = matcher.group(2); insertRoom(Integer.parseInt(rid), rname); } } rooms_label.setText("當(dāng)前房間數(shù):"+rooms_map.size()); } /** * 刪除一個(gè)room * @param content */ public void delRoom(String content){ if(content.length()>0){ int delRoomId = Integer.parseInt(content); Set<String> set = rooms_map.keySet(); Iterator<String> iter = set.iterator(); String rname=null; while(iter.hasNext()){ rname = iter.next(); if(rooms_map.get(rname)==delRoomId){ rooms_model.removeElement(rname); break; } } rooms_map.remove(rname); } rooms_label.setText("當(dāng)前房間數(shù):"+rooms_map.size()); } /** * 列出目前所有的rooms * @param content */ public void listRooms(String content){ String rname = null; String rid=null; Pattern rough_pattern=null; Matcher rough_matcher=null; Pattern detail_pattern=null; if(content.length()>0){ rough_pattern = Pattern.compile("<room>(.*?)</room>"); rough_matcher = rough_pattern.matcher(content); while(rough_matcher.find()){ String detail = rough_matcher.group(1); detail_pattern = Pattern.compile("<rname>(.*)</rname><rid>(.*)</rid>"); Matcher detail_matcher = detail_pattern.matcher(detail); if(detail_matcher.find()){ rname = detail_matcher.group(1); rid = detail_matcher.group(2); insertRoom(Integer.parseInt(rid), rname); } } } rooms_label.setText("當(dāng)前房間數(shù):"+rooms_map.size()); } /** * 插入一個(gè)room * @param rid * @param rname */ private void insertRoom(Integer rid, String rname){ if(!rooms_map.containsKey(rname)){ rooms_map.put(rname, rid); rooms_model.addElement(rname); }else{ rooms_map.remove(rname); rooms_model.removeElement(rname); rooms_map.put(rname, rid); rooms_model.addElement(rname); } rooms_label.setText("當(dāng)前房間數(shù):"+rooms_map.size()); } /** * 插入一個(gè)user * @param id * @param name */ private void insertUser(Integer id, String name){ if(!users_map.containsKey(name)){ users_map.put(name, id); users_model.addElement(name); }else{ users_map.remove(name); users_model.removeElement(name); users_map.put(name, id); users_model.addElement(name); } users_label.setText("房間內(nèi)人數(shù):"+users_map.size()); } /** * 獲得用戶的姓名 * @param strId * @return */ private String getUserName(String strId){ int uid = Integer.parseInt(strId); Set<String> set = users_map.keySet(); Iterator<String> iterator = set.iterator(); String cur=null; while(iterator.hasNext()){ cur = iterator.next(); if(users_map.get(cur)==uid){ return cur; } } return ""; } /** * 獲得用戶所在房間的名稱 * @param strId * @return */ private String getRoomName(String strId){ int rid = Integer.parseInt(strId); Set<String> set = rooms_map.keySet(); Iterator<String> iterator = set.iterator(); String cur = null; while(iterator.hasNext()){ cur = iterator.next(); if(rooms_map.get(cur)==rid){ return cur; } } return ""; } /** * 打印一條消息,如果有圖片就打印圖片,否則打印content * @param scrollPane * @param textPane * @param icon_code * @param title * @param content */ private void insertMessage(JScrollPane scrollPane, JTextPane textPane, String icon_code, String title, String content){ StyledDocument document = textPane.getStyledDocument(); /*獲取textpane中的文本*/ /*設(shè)置標(biāo)題的屬性*/ SimpleAttributeSet title_attr = new SimpleAttributeSet(); StyleConstants.setBold(title_attr, true); StyleConstants.setForeground(title_attr, Color.BLUE); /*設(shè)置正文的屬性*/ SimpleAttributeSet content_attr = new SimpleAttributeSet(); StyleConstants.setBold(content_attr, false); StyleConstants.setForeground(content_attr, Color.BLACK); Style style = null; if(icon_code!=null){ Icon icon = new ImageIcon("icon/"+icon_code+".png"); style = document.addStyle("icon", null); StyleConstants.setIcon(style, icon); } try { document.insertString(document.getLength(), title+"\n", title_attr); if(style!=null) document.insertString(document.getLength(), "\n", style); else document.insertString(document.getLength(), " "+content+"\n", content_attr); } catch (BadLocationException ex) { System.out.println("Bad location exception"); } /*設(shè)置滑動(dòng)條到最后*/ vertical.setValue(vertical.getMaximum()); } /** * 設(shè)置需要美化字體的組件 */ public static void setUIFont() { Font f = new Font("微軟雅黑", Font.PLAIN, 14); String names[]={ "Label", "CheckBox", "PopupMenu","MenuItem", "CheckBoxMenuItem", "JRadioButtonMenuItem","ComboBox", "Button", "Tree", "ScrollPane", "TabbedPane", "EditorPane", "TitledBorder", "Menu", "TextArea","TextPane", "OptionPane", "MenuBar", "ToolBar", "ToggleButton", "ToolTip", "ProgressBar", "TableHeader", "Panel", "List", "ColorChooser", "PasswordField","TextField", "Table", "Label", "Viewport", "RadioButtonMenuItem","RadioButton", "DesktopPane", "InternalFrame" }; for (String item : names) { UIManager.put(item+ ".font",f); } } /** * 設(shè)置UI風(fēng)格為當(dāng)前系統(tǒng)的風(fēng)格 */ public static void setUIStyle(){ String lookAndFeel =UIManager.getSystemLookAndFeelClassName(); try { UIManager.setLookAndFeel(lookAndFeel); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (UnsupportedLookAndFeelException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 測(cè)試用的main函數(shù) * @param args */ public static void main(String[] args) { Client client = new Client(); } }
ClientThread
package Client; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * * @author lannooo * */ public class ClientThread extends Thread{ private Socket socket; private Client client; private BufferedReader br; private PrintWriter pw; /** * 從過主線程傳入的socket和client對(duì)象來構(gòu)造 * @param socket * @param client */ public ClientThread(Socket socket, Client client){ this.client = client; this.socket = socket; try { br=new BufferedReader(new InputStreamReader(socket.getInputStream())); } catch (IOException e) { System.out.println("cannot get inputstream from socket."); } } /** * 不斷的讀數(shù)據(jù)并處理 * 調(diào)用主線程的方法來處理:client.method(); */ public void run() { try{ br=new BufferedReader(new InputStreamReader(socket.getInputStream())); while(true){ String msg = br.readLine(); parseMessage(msg); } }catch (Exception e) { e.printStackTrace(); } } /** * 處理從服務(wù)器收到的消息 * @param message */ public void parseMessage(String message){ String code = null; String msg=null; /* * 先用正則表達(dá)式匹配code碼和msg內(nèi)容 */ if(message.length()>0){ Pattern pattern = Pattern.compile("<code>(.*)</code>"); Matcher matcher = pattern.matcher(message); if(matcher.find()){ code = matcher.group(1); } pattern = Pattern.compile("<msg>(.*)</msg>"); matcher = pattern.matcher(message); if(matcher.find()){ msg = matcher.group(1); } System.out.println(code+":"+msg); switch(code){ case "1": /*一個(gè)普通消息處理*/ client.updateTextArea(msg); break; case "2": /*退出消息*/ client.showEscDialog(msg); break; case "3": /*列出房間*/ client.listRooms(msg); break; case "4": /*其他用戶的消息*/ client.updateTextAreaFromUser(msg); break; case "5": /*普通消息處理*/ client.updateTextArea(msg); break; case "11": /*添加用戶*/ client.addUser(msg); break; case "12": /*刪除用戶*/ client.delUser(msg); break; case "13": /*刪除房間*/ client.delRoom(msg); break; case "15": /*添加房間*/ client.addRoom(msg); break; case "16": /*更新用戶名稱*/ client.updateUser(msg); break; case "21": /*列出用戶列表*/ client.listUsers(msg); break; } } } }
IconDialog(選擇表情界面)
package Client; import java.awt.Container; import java.awt.Dialog; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; /** * * @author lannooo * */ public class IconDialog implements ActionListener { private JDialog dialog; private Client client; /** * 通過frame和客戶端對(duì)象來構(gòu)造 * @param frame * @param client */ public IconDialog(JFrame frame, Client client) { this.client = client; dialog = new JDialog(frame, "請(qǐng)選擇表情", true); /*16個(gè)表情*/ JButton[] icon_button = new JButton[16]; ImageIcon[] icons = new ImageIcon[16]; /*獲得彈出窗口的容器,設(shè)置布局*/ Container dialogPane = dialog.getContentPane(); dialogPane.setLayout(new GridLayout(0, 4)); /*加入表情*/ for(int i=1; i<=15; i++){ icons[i] = new ImageIcon("icon/"+i+".png"); icons[i].setImage(icons[i].getImage().getScaledInstance(50, 50, Image.SCALE_DEFAULT)); icon_button[i] = new JButton(""+i, icons[i]); icon_button[i].addActionListener(this); dialogPane.add(icon_button[i]); } dialog.setBounds(200,266,266,280); dialog.show(); } @Override public void actionPerformed(ActionEvent e) { /*構(gòu)造emoji結(jié)構(gòu)的消息發(fā)送*/ String cmd = e.getActionCommand(); System.out.println(cmd); dialog.dispose(); client.sendMsg("<emoji>"+cmd+"</emoji>", "message"); } }
關(guān)于怎么在Java中利用GUI編程實(shí)現(xiàn)在線聊天室就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。