前言:
用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;