java swing 聊天表情功能的实现(带完整代码)

 

前言:

 用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.*; 
/** 
  *   

Title: pictures

* *

Description:

* *

Copyright: Copyright (c) 2011

* *

Company:

* * @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
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 myPicInfo = new LinkedList();

 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 myPicInfo = new LinkedList();
	private List receivdPicInfo = new LinkedList();
	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; 

如果要带图片的,请留言

你可能感兴趣的:(java)