前言:
用java swing做聊天程序,可能是沒事找事,也可能是大才小用
不過作爲畢業設計還是綽綽有餘了,既然選擇了就做吧
其中比較重要的功能可能就是是聊天表情了,當然字體也重要
經過多天來的資料查找和實踐,終於做出來了,方法不是很先進,但是運行效果良好
下面是總結出的一個示例
主要功能:
1.聊天表情框的顯示,
2.聊天信息(文本信息、字體信息、表情信息、用戶)的傳輸udp,
3.聊天信息的顯示(表情和文本混合顯示),
4.震動,
5.懸浮提示框
難點1:表情框
難點2:表情(信息)的顯示和傳輸
難點3:表情和文本的混合
難點4:字體屬性的設置和傳輸
解決方案
一、表情框
使用javax.swing.JWindow類(無修飾的窗體類),繼承這個類可以實現,在一個什麼都沒有的空窗體(沒有標題欄,沒有最大、最小化,沒有關閉按鈕等),我們可以設置其佈局爲網格佈局setLayout(ew GridLayout(7,15) ),然後在每個格子裏再加上圖片(圖片當然要先放在JLabel或JPanel裏比較好,設置邊框,添加鼠標監聽)等
代碼如下:
表情圖片所在目錄:com.zou.chat.qqdefaultface 下放105張表情圖 名字爲0.gif,1.gif——104.gif
注意目錄結構:com.zou.chat.qqdefaultface 下面放圖,com.zou.chat下放java文件,紅色標註部分保證一致
PicsJWindow.java:
package com.zou.chat;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
/**
* <p> Title: pictures</p>
*
* <p> Description: </p>
*
* <p> Copyright: Copyright (c) 2011 </p>
*
* <p> Company: </p>
*
* @author not attributable
* @version 1.0
*/
public class PicsJWindow extends JWindow {
private static final long serialVersionUID = 1L;
GridLayout gridLayout1 = new GridLayout(7,15);
JLabel[] ico=new JLabel[105]; /*放表情*/
int i;
ChatFrame owner;
String[] intro = {"","","","","","","","","","","","","","","",
"","","","","","","","","","","","","","","",
"","","","","","","","","","","","","","","",
"","","","","","","","","","","","","","","",
"","","","","","","","","","","","","","","",
"","","","","","","","","","","","","","","",
"","","","","","","","","","","","","","","",};/*圖片描述*/
public PicsJWindow(ChatFrame owner) {
super(owner);
this.owner=owner;
try {
init();
this.setAlwaysOnTop(true);
}
catch (Exception exception) {
exception.printStackTrace();
}
}
private void init() throws Exception {
this.setPreferredSize(new Dimension(28*15,28*7));
JPanel p = new JPanel();
p.setOpaque(true);
this.setContentPane(p);
p.setLayout(gridLayout1);
p.setBackground(SystemColor.text);
String fileName = "";
for(i=0;i <ico.length;i++){
fileName= "qqdefaultface/"+i+".gif";/*修改圖片路徑*/
ico[i] =new JLabel(new ChatPic(PicsJWindow.class.getResource(fileName),i),SwingConstants.CENTER);
ico[i].setBorder(BorderFactory.createLineBorder(new Color(225,225,225), 1));
ico[i].setToolTipText(i+"");
ico[i].addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e){
if(e.getButton()==1){
JLabel cubl = (JLabel)(e.getSource());
ChatPic cupic = (ChatPic)(cubl.getIcon());
owner.insertSendPic(cupic);
cubl.setBorder(BorderFactory.createLineBorder(new Color(225,225,225), 1));
getObj().dispose();
}
}
@Override
public void mouseEntered(MouseEvent e) {
((JLabel)e.getSource()).setBorder(BorderFactory.createLineBorder(Color.BLUE));
}
@Override
public void mouseExited(MouseEvent e) {
((JLabel)e.getSource()).setBorder(BorderFactory.createLineBorder(new Color(225,225,225), 1));
}
});
p.add(ico[i]);
}
p.addMouseListener(new MouseAdapter(){
@Override
public void mouseExited(MouseEvent e) {
getObj().dispose();
}
});
}
@Override
public void setVisible(boolean show) {
if (show) {
determineAndSetLocation();
}
super.setVisible(show);
}
private void determineAndSetLocation() {
Point loc = owner.getPicBtn().getLocationOnScreen();/*控件相對於屏幕的位置*/
setBounds(loc.x-getPreferredSize().width/3, loc.y-getPreferredSize().height,
getPreferredSize().width, getPreferredSize().height);
}
private JWindow getObj(){
return this;
}
}
ChatPic.java
/*自定義的ImageIcon的子類,在聊天窗體添加表情時,方便取出圖片的描述信息*/
package com.zou.chat; import java.net.URL;
import javax.swing.ImageIcon; public class ChatPic extends ImageIcon{ /** *圖片描述 */ private static final long serialVersionUID = 1L; int im;//圖片代號 public int getIm() { return im; } public void setIm(int im) { this.im = im; } public ChatPic(URL url,int im){ super(url); this.im = im; } }
二、三、四 :表情(信息)的顯示和傳輸,表情和文本的混合 字體屬性的設置和傳輸
顯示錶情通過 JTextPane的StyledDocument對象的添加圖片功能實現,傳輸時不直接傳圖片,只傳相對於文本的位置和代號,通過document取出表情的位置和代號,自定義封裝規則(這樣對方程序收到後就可以在按規則還原位置和代號,在指定位置顯示圖片),這樣表情和文本就可以混合了,字體一樣定義傳輸規則,比如消息串可以這樣定義的 userinfo*font*message
用String.spilt()分離
userinfo代表 用戶信息字符串表示,爲了簡單就代表用戶名,當然其中不能包括*號,不然 把userinfo*font*message 根據*分離出來就不能取得正確的userinfo,
font字體信息 font的可爲內容爲 fontname|fontsize|fontColor ”宋體| 10|1-1-1",可以繼續取出fontname fontsize fontColor ,用其設置要插入的文字的格式
JTextPane的使用:
JTextPane tp = new JTextPane();
StyledDocument doc = jp.getStyledDocument();//這個對象可以完成下列操作,很方便,實現圖文混和顯示
既可以插入文本和文本樣式,
SimpleAttributeSet attrSet;//參數需要
doc .insertString(doc .getLength(), "Hello,SytedDocument!\n",attrib.getAttrSet());
public SimpleAttributeSet getAttrSet() {
attrSet = new SimpleAttributeSet();
StyleConstants.setFontFamily(attrSet, "宋體");
StyleConstants.setBold(attrSet, false);
StyleConstants.setItalic(attrSet, false);
StyleConstants.setFontSize(attrSet, size);
StyleConstants.setForeground(attrSet, Color.RED);
return attrSet;
}
也可以插入圖片,
String fileName= "qqdefaultface/1.gif";
doc .insertIcon(new ImageIcon(PicsJWindow.class.getResource(fileName)));
還可以設置每次插入的文字位置,
int pos = 0; // 位置0到 長度減1,不要越界
doc .setCaretPosition(pos); /*設置插入位置*/
doc .insertString(doc .getLength(), "Hello,SytedDocument!\n",attrib.getAttrSet());
圖片位置,
doc .setCaretPosition(pos);/*設置插入位置*/
doc .insertIcon(new ImageIcon(PicsJWindow.class.getResource(fileName)));
也可以取出每次插入的文本和圖片信息
String text = tp.getText();//取文本
//取圖
private List<PicInfo> myPicInfo = new LinkedList<PicInfo>();
private String buildPicInfo(){
StringBuilder sb = new StringBuilder("");
//遍歷jtextpane找出所有的圖片信息封裝成指定格式
for(int i = 0; i < this.jpMsg.getText().length(); i++){
if(docMsg.getCharacterElement(i).getName().equals("icon")){
//doc = (ChatPic)
Icon icon = StyleConstants.getIcon(doc.getStyledDocument().getCharacterElement(i).getAttributes());
ChatPic cupic = (ChatPic)icon;
PicInfo picInfo= new PicInfo(i,cupic.getIm()+"");
myPicInfo.add(picInfo);
sb.append(i+"&"+cupic.getIm()+"+");
}
}
System.out.println(sb.toString());
return sb.toString();
//return null;
}
後續代碼:ChatFrame.java
package com.zou.chat; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Frame; import java.awt.Point; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.LinkedList; import java.util.List; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextPane; import javax.swing.ScrollPaneConstants; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.plaf.FontUIResource; import javax.swing.text.BadLocationException; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; /** * 聊天窗體類 * @author yangxing zou * @version 0.1 */ public class ChatFrame extends JFrame implements MouseListener{ private static final long serialVersionUID = 1L; public final int fwidth = 550; public final int fheight = 500; /* 左邊和右邊要顯示的界面 */ public JLabel left = new JLabel(); JScrollPane jspChat; /*聊天內容*/ private JTextPane jpChat; /*要發送的內容*/ private JTextPane jpMsg; JScrollPane jspMsg; /* 插入文字樣式用*/ private StyledDocument docChat = null; private StyledDocument docMsg = null; private JButton btnSend; /*好友的ip*/ /*private String friendIP;*/ /*好友接收消息的端口*/ /*private int friendPort;*/ /*字體名稱;字號大小;文字顏色*/ private JComboBox fontName = null, fontSize = null,fontColor = null; /*插入按鈕;清除按鈕;插入圖片按鈕*/ private JButton b_shake=null,b_pic, b_remove = null; private static final Color TIP_COLOR = new Color(255, 255, 225); /* 錯誤信息氣泡提示*/ private CoolToolTip error_tip; /*表情框*/ private PicsJWindow picWindow; private List<PicInfo> myPicInfo = new LinkedList<PicInfo>(); private List<PicInfo> receivdPicInfo = new LinkedList<PicInfo>(); class PicInfo{ /* 圖片信息*/ int pos; String val; public PicInfo(int pos,String val){ this.pos = pos; this.val = val; } public int getPos() { return pos; } public void setPos(int pos) { this.pos = pos; } public String getVal() { return val; } public void setVal(String val) { this.val = val; } } public JButton getPicBtn(){ return b_pic; } public ChatFrame() { init(); } /** * 插入圖片 * * @param icon */ public void insertSendPic(ImageIcon imgIc) { //jpMsg.setCaretPosition(docChat.getLength()); // 設置插入位置 jpMsg.insertIcon(imgIc); // 插入圖片 System.out.print(imgIc.toString()); //insert(new FontAttrib()); // 這樣做可以換行 } /* * 重組收到的表情信息串 */ public void receivedPicInfo(String picInfos){ String[] infos = picInfos.split("[+]"); for(int i = 0 ; i < infos.length ; i++){ String[] tem = infos[i].split("[&]"); if(tem.length==2){ PicInfo pic = new PicInfo(Integer.parseInt(tem[0]),tem[1]); receivdPicInfo.add(pic); } } } /** * 重組發送的表情信息 * @return 重組後的信息串 格式爲 位置|代號+位置|代號+…… */ private String buildPicInfo(){ StringBuilder sb = new StringBuilder(""); //遍歷jtextpane找出所有的圖片信息封裝成指定格式 for(int i = 0; i < this.jpMsg.getText().length(); i++){ if(docMsg.getCharacterElement(i).getName().equals("icon")){ //ChatPic = (ChatPic) Icon icon = StyleConstants.getIcon(jpMsg.getStyledDocument().getCharacterElement(i).getAttributes()); ChatPic cupic = (ChatPic)icon; PicInfo picInfo= new PicInfo(i,cupic.getIm()+""); myPicInfo.add(picInfo); sb.append(i+"&"+cupic.getIm()+"+"); } } System.out.println(sb.toString()); return sb.toString(); //return null; } /** * 初始化窗體 */ private void init() { setLayout(new BorderLayout()); /*this.setUndecorated(true);*/ /* getRootPane().setWindowDecorationStyle(JRootPane.FRAME); */ setSize(fwidth, fheight); this.setMinimumSize(new Dimension(fwidth, fheight)); this.getContentPane().setBackground(Color.GRAY); setResizable(false); setLocationRelativeTo(null); this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(1); } }); this.addComponentListener(new ComponentAdapter(){ @Override public void componentResized(ComponentEvent e) { ChatFrame.this.picWindow.dispose(); } @Override public void componentMoved(ComponentEvent e) { ChatFrame.this.picWindow.dispose(); } @Override public void componentHidden(ComponentEvent e) { ChatFrame.this.picWindow.dispose(); } }); /*窗體前置*/ setAlwaysOnTop(true); /*聊天信息框*/ jpChat = new JTextPane(); jpChat.addMouseListener(this); jpChat.setEditable(false); jspChat = new JScrollPane(jpChat, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); /*用戶聊天信息輸入框*/ jpMsg = new JTextPane(); jpMsg.addMouseListener(this); jspMsg = new JScrollPane(jpMsg, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); jspMsg.setPreferredSize(new Dimension(100, 100)); error_tip = new CoolToolTip(this, jspMsg, TIP_COLOR, 3, 10); /*發送按鈕*/ btnSend = new JButton("發送"); btnSend.setFocusable(false); /*獲得JTextPane的Document用於設置字體*/ docChat = jpChat.getStyledDocument(); docMsg = jpMsg.getStyledDocument(); /*添加鼠標事件監聽*/ btnSend.addMouseListener(this); /*字體區*/ JLabel lblSend = new JLabel(); lblSend.setLayout(new FlowLayout(FlowLayout.RIGHT)); String[] str_name = { "宋體", "黑體", "Dialog", "Gulim" }; String[] str_Size = { "12", "14", "18", "22", "30", "40" }; //String[] str_Style = { "常規", "斜體", "粗體", "粗斜體" }; String[] str_Color = { "黑色", "紅色", "藍色", "黃色", "綠色" }; fontName = new JComboBox(str_name); fontSize = new JComboBox(str_Size); fontColor = new JComboBox(str_Color); Box box = Box.createHorizontalBox(); box.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); box.add(new JLabel("字體:")); box.add(fontName); box.add(Box.createHorizontalStrut(3)); box.add(new JLabel("字號:")); box.add(fontSize); box.add(Box.createHorizontalStrut(3)); box.add(new JLabel("顏色:")); box.add(fontColor); box.add(Box.createHorizontalStrut(3)); box.add(btnSend); JPanel PaneLeftSouth = new JPanel(); PaneLeftSouth.setLayout(new BorderLayout()); b_pic = new JButton("表情"); b_pic.setFocusable(false); b_shake = new JButton("震動"); b_shake.setFocusable(false); b_remove = new JButton("清空"); b_remove.setFocusable(false); picWindow = new PicsJWindow(this); b_pic.addMouseListener(this); b_remove.addMouseListener(this); b_shake.addMouseListener(this); Box box_1 = Box.createHorizontalBox(); box_1.add(b_pic); box_1.add(b_shake); box_1.add(b_remove); PaneLeftSouth.add(box_1, BorderLayout.NORTH);//字體、表情、震動 PaneLeftSouth.add(jspMsg, BorderLayout.CENTER); PaneLeftSouth.add(box, BorderLayout.SOUTH); left.setLayout(new BorderLayout()); left.setOpaque(false); PaneLeftSouth.setBackground(Color.CYAN); left.add(jspChat, BorderLayout.CENTER); left.add(PaneLeftSouth, BorderLayout.SOUTH); add(left, BorderLayout.CENTER); new receivMsgThread().start(); } @Override public void mouseClicked(MouseEvent arg0) {} @Override public void mouseEntered(MouseEvent arg0) { error_tip.setVisible(false); } @Override public void mouseExited(MouseEvent arg0) {} @Override public void mousePressed(MouseEvent e) { picWindow.setVisible(false); } @Override public void mouseReleased(MouseEvent e) { if (getY() <= 0) { setLocation(getX(), 0); } if (e.getButton() != 1) return;/*不是左鍵*/ JComponent source = (JComponent) e.getSource(); /*鼠標釋放時在事件源內,才響應單擊事件*/ if (e.getX() >= 0 && e.getX() <= source.getWidth() && e.getY() >= 0 && e.getY() <= source.getHeight()) { if (source == btnSend){ sendMsg(); }else if (source == this.b_shake){ sendShake(); } else if (source == this.b_pic){ picWindow.setVisible(true); } else if (source == this.b_remove){ jpChat.setText(""); jpChat.revalidate(); } } } /** * 向好友發送震動 */ public void sendShake(){ String uname = Sender.localIP+":"+Sender.SendPort; if (!Sender.sendUDPMsg(MsgType.SHAKE, uname, Sender.localIP, Sender.SendPort,"shake")){ error_tip.setText("發送失敗!"); error_tip.setVisible(true); } insert("你向 "+ uname +" 發送了一個震動!"); } /** * 震動三秒以上 * @param uname */ public void shake(String uname){ setExtendedState(Frame.NORMAL); setVisible(true); insert(uname+" 給你發了一個震動!"); new Thread(){ long begin = System.currentTimeMillis(); long end = System.currentTimeMillis(); Point p = ChatFrame.this.getLocationOnScreen(); public void run(){ int i = 1; while((end-begin)/1000<3){ ChatFrame.this.setLocation(new Point((int)p.getX()-5*i,(int)p.getY()+5*i)); end = System.currentTimeMillis(); try { Thread.sleep(5); i=-i; ChatFrame.this.setLocation(p); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } /** * 發送消息 */ private FontAndText myFont = null; public void sendMsg() { String message = jpMsg.getText(); if (message.length() == 0) { error_tip.setText("請輸入聊天信息!"); error_tip.setVisible(true); return; } if(message.length()>100){ error_tip.setText("消息最多一百個字符!你要發送的爲"+message.length() + "個字符!"); error_tip.setVisible(true); return; } String uname = Sender.localIP+":"+Sender.chatPort; myFont = getFontAttrib(); if (Sender.sendUDPMsg(MsgType.CHAT, uname, Sender.localIP, Sender.SendPort, myFont.toString())) { addMeg(uname); this.jpMsg.setText(""); } else { error_tip.setText("發送消息失敗!"); error_tip.setVisible(true); } } /** * 追加新消息到聊天窗體 */ SimpleDateFormat sf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss"); FontAndText dateFont = new FontAndText("","宋體",20,Color.BLUE); public void addMeg(String uname) { String msg = uname + " " + sf.format(new Date()); dateFont.setText(msg); insert(dateFont); pos2 = jpChat.getCaretPosition(); myFont.setText(jpMsg.getText()); insert(myFont); insertPics(false); } public void addRecMsg(String uname,String message){ setExtendedState(Frame.NORMAL); //setVisible(true); String msg = uname + " " + sf.format(new Date()); dateFont.setText(msg); insert(dateFont);/*時間和用戶信息*/ int index = message.lastIndexOf("*"); System.out.println("index="+index); /*很重要的,記錄下聊天區域要插入聊天消息的開始位置,*/ pos1 = jpChat.getCaretPosition(); if(index>0 && index < message.length()-1){/*存在表情信息*/ FontAndText attr = getRecivedFont(message.substring(0,index)); insert(attr); receivedPicInfo(message.substring(index+1,message.length())); insertPics(true); }else{ FontAndText attr = getRecivedFont(message); insert(attr); } } /** * 將收到的消息轉化爲自定義的字體類對象 * @param message 收到的聊天信息 * @return 字體類對象 */ public FontAndText getRecivedFont(String message){ String[] msgs = message.split("[|]"); String fontName = ""; int fontSize = 0; String[] color; String text = message; Color fontC = new Color(222,222,222); if(msgs.length>=4){/*這裏簡單處理,表示存在字體信息*/ fontName = msgs[0]; fontSize = Integer.parseInt(msgs[1]); color = msgs[2].split("[-]"); if(color.length==3){ int r = Integer.parseInt(color[0]); int g = Integer.parseInt(color[1]); int b = Integer.parseInt(color[2]); fontC = new Color(r,g,b); } text = ""; for(int i = 3; i < msgs.length ; i++){ text = text + msgs[i]; } } FontAndText attr = new FontAndText(); attr.setName(fontName); attr.setSize(fontSize); attr.setColor(fontC); attr.setText(text); System.out.println("getRecivedFont(String message):"+attr.toString()); return attr; } /** * 插入圖片 * * @param isFriend 是否爲朋友發過來的消息 */ int pos1; int pos2; private void insertPics(boolean isFriend) { if(isFriend){ if(this.receivdPicInfo.size()<=0){ return; }else{ for(int i = 0 ; i < receivdPicInfo.size() ; i++){ PicInfo pic = receivdPicInfo.get(i); String fileName; jpChat.setCaretPosition(pos1+pic.getPos()); /*設置插入位置*/ fileName= "qqdefaultface/"+pic.getVal()+".gif";/*修改圖片路徑*/ jpChat.insertIcon(new ImageIcon(PicsJWindow.class.getResource(fileName))); /*插入圖片*/ /* jpChat.updateUI();*/ } receivdPicInfo.clear(); } }else{ if(myPicInfo.size()<=0){ return; }else{ for(int i = 0 ; i < myPicInfo.size() ; i++){ PicInfo pic = myPicInfo.get(i); jpChat.setCaretPosition(pos2+pic.getPos()); /*設置插入位置*/ String fileName; fileName= "qqdefaultface/"+pic.getVal()+".gif";/*修改圖片路徑*/ jpChat.insertIcon(new ImageIcon(PicsJWindow.class.getResource(fileName))); /*插入圖片*/ /*jpChat.updateUI();*/ } myPicInfo.clear(); } } jpChat.setCaretPosition(docChat.getLength()); /*設置滾動到最下邊*/ //insert(new FontAttrib()); /*這樣做可以換行*/ } /** * 將帶格式的文本插入JTextPane * * @param attrib */ private void insert(FontAndText attrib) { try { // 插入文本 docChat.insertString(docChat.getLength(), attrib.getText() + "\n", attrib.getAttrSet()); jpChat.setCaretPosition(docChat.getLength()); // 設置滾動到最下邊 } catch (BadLocationException e) { e.printStackTrace(); } } private void insert(String text) { try { // 插入文本 docChat.insertString(docChat.getLength(), text + "\n", dateFont.getAttrSet()); jpChat.setCaretPosition(docChat.getLength()); // 設置插入位置 } catch (BadLocationException e) { e.printStackTrace(); } } /** *字體屬性輔助類 */ class FontAndText{ public static final int GENERAL = 0; // 常規 private String msg = "", name = "宋體"; // 要輸入的文本和字體名稱 private int size = 0; //字號 private Color color = new Color(225,225,225); // 文字顏色 private SimpleAttributeSet attrSet = null; // 屬性集 /** * 一個空的構造(可當做換行使用) */ public FontAndText() { } public FontAndText(String msg,String fontName,int fontSize,Color color){ this.msg = msg; this.name = fontName; this.size = fontSize; this.color = color; } /** * 返回屬性集 * * @return */ public SimpleAttributeSet getAttrSet() { attrSet = new SimpleAttributeSet(); if (name != null){ StyleConstants.setFontFamily(attrSet, name); } StyleConstants.setBold(attrSet, false); StyleConstants.setItalic(attrSet, false); StyleConstants.setFontSize(attrSet, size); if (color != null) StyleConstants.setForeground(attrSet, color); return attrSet; } public String toString(){ //將消息分爲四塊便於在網絡上傳播 return name+"|" +size+"|" +color.getRed()+"-"+color.getGreen()+"-"+color.getBlue()+"|" +msg; } public String getText() { return msg; } public void setText(String text) { this.msg = text; } public Color getColor() { return color; } public void setColor(Color color) { this.color = color; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } } /** * 獲取所需要的文字設置 * * @return FontAttrib */ private FontAndText getFontAttrib() { FontAndText att = new FontAndText(); att.setText(jpMsg.getText()+"*"+buildPicInfo());//文本和表情信息 att.setName((String) fontName.getSelectedItem()); att.setSize(Integer.parseInt((String) fontSize.getSelectedItem())); String temp_color = (String) fontColor.getSelectedItem(); if (temp_color.equals("黑色")) { att.setColor(new Color(0, 0, 0)); } else if (temp_color.equals("紅色")) { att.setColor(new Color(255, 0, 0)); } else if (temp_color.equals("藍色")) { att.setColor(new Color(0, 0, 255)); } else if (temp_color.equals("黃色")) { att.setColor(new Color(255, 255, 0)); } else if (temp_color.equals("綠色")) { att.setColor(new Color(0, 255, 0)); } return att; } private class receivMsgThread extends Thread{/*接收在線好友和陌生人消息*/ DatagramSocket chatSoc = null; public receivMsgThread(){ try { chatSoc = new DatagramSocket(Sender.chatPort); } catch (SocketException e) { e.printStackTrace(); System.exit(1); } } @Override public void run(){ while(true){ try { byte[] bytes=new byte[1024*128]; DatagramPacket dp=new DatagramPacket(bytes, bytes.length); chatSoc.receive(dp); String recStr = new String(bytes, 0, dp.getLength(), "UTF-8"); //System.out.println("GroupsPanel recStr = " + recStr); String[] strs = recStr.split("[*]"); int msgType; if(strs.length>=3){ msgType = Integer.parseInt(strs[0]); }else{ /*System.out.println("不是好友發過來的聊天信息!");*/ continue; } String uname = strs[1]; String message = strs[2]; if(strs.length>3){ for(int i = 3; i < strs.length ; i++){ message = message + "*" + strs[i]; } } if(msgType == MsgType.CHAT){ ChatFrame.this.addRecMsg(uname,message); }else if(msgType == MsgType.SHAKE){ ChatFrame.this.shake(uname); } } catch (Exception e) { JOptionPane.showMessageDialog(ChatFrame.this, "系統運行出錯!"); e.printStackTrace(); } } } } public static void setUIFont(FontUIResource f) { /* sets the default font for all Swing components. ex.setUIFont (new javax.swing.plaf.FontUIResource("Serif",Font.ITALIC,12));*/ java.util.Enumeration keys = UIManager.getDefaults().keys(); while (keys.hasMoreElements()) { Object key = keys.nextElement(); Object value = UIManager.get(key); if (value instanceof FontUIResource) UIManager.put(key, f); } } public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException { /* UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); */ /*和系統的觀感保持一致*/ setUIFont(new FontUIResource("宋體", Font.PLAIN, 15)); new ChatFrame().setVisible(true); } }
CoolToolTip.java
package com.zou.chat;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Point;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.Icon;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
/**
* 氣泡提示框類
*/
public class CoolToolTip extends JPanel {
private static final long serialVersionUID = 1L;
private JLabel label = new JLabel();
private boolean haveShowPlace;
private Component attachedCom; // 要顯示提示氣泡的組件
private Component parentWindow; // 要顯示提示氣泡的組件的窗體
public CoolToolTip(Component parent,Component attachedComponent, Color fillColor,
int borderWidth, int offset) {
this.parentWindow = parent;
this.attachedCom = attachedComponent;
label.setBorder(new EmptyBorder(borderWidth, borderWidth, borderWidth,
borderWidth));
label.setBackground(fillColor);
label.setOpaque(true);
label.setFont(new Font("system", 0, 12));
setOpaque(false);
// this.setBorder(new BalloonBorder(fillColor, offset));
this.setLayout(new BorderLayout());
add(label);
setVisible(false);
// 當氣泡顯示時組件移動,氣泡也跟着移動
this.attachedCom.addComponentListener(new ComponentAdapter() {
@Override
public void componentMoved(ComponentEvent e) {
if (isShowing()) {//懸浮提示 顯示了的,重新設置位置
determineAndSetLocation();
}
}
});
}
private void determineAndSetLocation() {
if(!attachedCom.isShowing()){
return;
}
Point loc = attachedCom.getLocationOnScreen(); //控件相對於屏幕的位置
Point paPoint = parentWindow.getLocationOnScreen(); //對應窗體相對於屏幕的位置
//System.out.println(attachedComponent.getLocationOnScreen());
setBounds(loc.x-paPoint.x, loc.y -paPoint.y - getPreferredSize().height,
getPreferredSize().width, getPreferredSize().height);
}
public void setText(String text) {
label.setText(text);
}
// 設置氣泡背景圖片
public void setIcon(Icon icon) {
label.setIcon(icon);
}
// 設置氣泡的文字和圖片間的距離
public void setIconTextGap(int iconTextGap) {
label.setIconTextGap(iconTextGap);
}
@Override
public void setVisible(boolean show) {
if (show) {
determineAndSetLocation();
findShowPlace();
}
super.setVisible(show);
}
private void findShowPlace() {
if (haveShowPlace) {
return;
}
// we use the popup layer of the top level container (frame or
// dialog) to show the balloon tip
// first we need to determine the top level container
JLayeredPane layeredPane = null;
if(parentWindow instanceof JDialog){
layeredPane = ((JDialog)parentWindow).getLayeredPane();
}else if(parentWindow instanceof JFrame){
layeredPane = ((JFrame)parentWindow).getLayeredPane();
}
/*Container parent = attachedCom.getParent();
while (true) {
if (parent instanceof JFrame) {
layeredPane = ((JFrame) parent).getLayeredPane();
break;
} else if (parent instanceof JDialog) {
layeredPane = ((JDialog) parent).getLayeredPane();
break;
}
parent = parent.getParent();
}*/
if(layeredPane!=null){
layeredPane.add(this, JLayeredPane.POPUP_LAYER);
haveShowPlace = true;
}
}
public Component getAttachedComponent() {
return attachedCom;
}
public void setAttachedComponent(Component attachedComponent) {
this.attachedCom = attachedComponent;
}
}
Sender.java
package com.zou.chat;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Random;
public class Sender {
/*本機ip*/
public static String localIP = "127.0.0.1";
/*服務器ip*/
public static String serverIP = null;
/*錯誤信息*/
public static String err_msg = "";
/*默認發送端口*/
public static int SendPort = 5555;
/*默認聊天端口*/
public static int chatPort = 6666;
/*接收信息的socket*/
/*private static DatagramSocket recDs = null;*/
/*private static DatagramSocket chatSoc = null;*/
static{
/*rm = new Random();*/
/*setLocalIP();*/
/*chatPort = generateUDPPort();*/
}
public Sender(){
/* try {
if(recDs == null){
rePort = generateUsefulPort();
recDs = new DatagramSocket(rePort);
}
} catch (SocketException e) {
e.printStackTrace();
}*/
//findServer();
}
/**
* @param msgType 消息類別
* @param uname
* @param friendIP
* @param friendPort
* @return 發送是否成功
*/
public static boolean sendUDPMsg(int msgType,String uname,String friendIP,int friendPort,String messae){
try
{
/*從命令行得到要發送的內容,使用UTF-8編碼將消息轉換爲字節*/
byte[] msg = (msgType+"*"+uname+"*"+messae).getBytes("UTF-8");
/*得到主機的internet地址*/
InetAddress address = InetAddress.getByName(friendIP);
/*用數據和地址初始化一個數據報分組(數據包)*/
DatagramPacket packet = new DatagramPacket(msg, msg.length, address,
friendPort);
/*創建一個默認的套接字,通過此套接字發送數據包*/
DatagramSocket dSocket = new DatagramSocket();
dSocket.send(packet);
/*發送完畢後關閉套接字*/
dSocket.close();
}
catch (Exception e)
{
e.printStackTrace();
err_msg = "系統運行異常!";
return false;
}
return true;
}
/**
* 獲取本機的IP
*/
/* private static void setLocalIP(){
try {
InetAddress address = InetAddress.getLocalHost();
localIP = address.getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
err_msg = "出錯:SendMessage.takeLocalIP():Exception!";
}
System.out.println("localIP = " + localIP);
}*/
/*private static Random rm;*/
/* private static int generateUsefulPort(){
int port = 1025 + rm.nextInt(5000);
try{
bindPort("0.0.0.0", port);
bindPort(InetAddress.getLocalHost().getHostAddress(),port);
}catch(Exception e){
e.printStackTrace();
return generateUsefulPort();
}
System.out.println("generateUsefulPort=" + port);
return port;
}*/
/* public static int generateUDPPort(){
rePort = 1024 + rm.nextInt(5000);
try {
DatagramSocket socket = new DatagramSocket(rePort);
socket.close();
} catch (IOException e) {
return generateUDPPort();
}
return rePort;
}*/
/**
* 檢測tcp端口是否可用
* @param host
* @param port
* @throws Exception
*/
/* private static void bindPort(String host, int port) throws Exception{
Socket s = new Socket();
s.bind(new InetSocketAddress(host, port));
s.close();
} */
}
MsgType.java
package com.zou.chat;
public final class MsgType {
public final static int CHAT = 0; //聊天
public final static int ONLINE = 1; //上線
public final static int OFFLINE = 2; //下線
public final static int SHAKE = 3; //震動
}
運行一個後,改Sender.java,中的 端口,互換端口,運行另一個
/*默認發送端口*/
public static int SendPort = 5555;
/*默認聊天端口*/
public static int chatPort = 6666;