📄 chatframe.java
字号:
package app.pane;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import draw.View;
import skin.Display;
import net.NetMessage;
import app.Application;
/**
* 聊天面板,可以附在响应画布上面
*
* @author Thihy
*
*/
public class ChatFrame extends JInternalFrame {
View view;
JPanel chatPane;
JTextArea chatField;
JPanel contentPane;
JTextPane chatContent;
JButton hideShowButton;
String chatText = "";
String userName = null;// 本机聊天者的名称,初始为null
Display display = new Display();
int chatPaneWidth = 300;
HashMap<String, String> codeStr;
boolean isHidden;
boolean canHide;
ImageIcon hideIcon = new ImageIcon(getClass()
.getResource("images/hide.gif")), showIcon = new ImageIcon(
getClass().getResource("images/show.gif"));
/**
* 初始化聊天面板
*
* @param view
* 聊天面板所在的画布
* @param canHide
* 能否隐藏
*/
public ChatFrame(View view, boolean canHide) {
super("ChatDemo");
this.view = view;
this.isHidden = false;
this.canHide = canHide;
chatPane = new JPanel();
chatPane.setBorder(new EdgedBorder(Color.LIGHT_GRAY));
chatPane.setLayout(new BorderLayout());
contentPane = creatContentPane();
contentPane.setVisible(!isHidden);
contentPane.setPreferredSize(new Dimension(chatPaneWidth, 50));
chatPane.add(contentPane, BorderLayout.CENTER);
chatPane.add(creatHideShowBar(), BorderLayout.WEST);
initCodeStr();
}
/**
* 初始化codeStr
*/
public void initCodeStr() {
codeStr = new HashMap<String, String>();
codeStr.put("惊讶", "0.gif");
codeStr.put("苦闷", "1.gif");
codeStr.put("痴情", "2.gif");
codeStr.put("发呆", "3.gif");
codeStr.put("帅气", "4.gif");
codeStr.put("想哭", "5.gif");
codeStr.put("美眉", "6.gif");
codeStr.put("呵呵", "7.gif");
codeStr.put("睡觉", "8.gif");
codeStr.put("大哭", "9.gif");
codeStr.put("发窘", "10.gif");
codeStr.put("发怒", "11.gif");
codeStr.put("闲适", "12.gif");
codeStr.put("嗤笑", "13.gif");
codeStr.put("微笑", "14.gif");
codeStr.put("smile", "14.gif");
}
/**
* 获取聊天面板
*
* @return 聊天面板
*/
public JPanel getChatPane() {
return chatPane;
}
/**
* 创建聊天面板的主面板<br/> 主要有 显示消息面板 输入消息面板
*
* @return 聊天面板的主面板
*/
private JPanel creatContentPane() {
final JPanel contentPane = new JPanel();
contentPane.setLayout(new BorderLayout());
// 聊天窗口标题
JPanel chatTitlePane = new JPanel();
chatTitlePane.setLayout(new FlowLayout(FlowLayout.LEFT));
JLabel chatTitle = new JLabel("聊天窗口");
chatTitlePane.add(chatTitle);
contentPane.add(chatTitlePane, BorderLayout.NORTH);
// 聊天显示消息栏
chatContent = new JTextPane();
chatContent.setEditable(false);
JScrollPane chatChontentScroller = new JScrollPane();
chatChontentScroller.setViewportView(chatContent);
chatChontentScroller
.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
contentPane.add(chatChontentScroller, BorderLayout.CENTER);
// 聊天输入栏
chatField = new JTextArea(5, 30);
chatField.setLayout(null);
chatField.setLineWrap(true);
chatField.setFont(new Font("宋体", Font.PLAIN, 16));
JScrollPane chatFieldScroller = new JScrollPane();
chatFieldScroller.setViewportView(chatField);
chatFieldScroller
.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
chatFieldScroller
.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
contentPane.add(chatFieldScroller, BorderLayout.SOUTH);
ChatAction chatAction = new ChatAction();
// 当点击enter则发送消息,ctrl+enter则回车
chatField.registerKeyboardAction(chatAction, "Send", KeyStroke
.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_FOCUSED);
chatField.registerKeyboardAction(chatAction, "Enter", KeyStroke
.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK),
JComponent.WHEN_FOCUSED);
return contentPane;
}
/**
* 切换隐藏 在显示和隐藏中切换,除非指定不能隐藏
*/
public void switchHideShow() {
if (isHidden) {// 当前是隐藏状态,现在切换至正常状态
contentPane.setVisible(true);
contentPane.setPreferredSize(new Dimension(chatPaneWidth, 50));
hideShowButton.setIcon(hideIcon);
isHidden = false;
} else if (canHide) {// 当前是正常状态,现在切换至隐藏状态
contentPane.setPreferredSize(new Dimension(chatPaneWidth, 50));
contentPane.setVisible(false);
hideShowButton.setIcon(showIcon);
isHidden = true;
}
}
/**
* 分割条上有一个按钮:在隐藏和显示中切换。使得主窗口的空间可以大一点。
*
* @return 分割条
*/
public JToolBar creatHideShowBar() {
JToolBar hideShowBar = new JToolBar();
hideShowBar.setOrientation(SwingConstants.VERTICAL);
hideShowBar.setFloatable(false);
hideShowBar.setMargin(new Insets(0, 0, 0, 0));
hideShowButton = new JButton();
hideShowButton.setMargin(new Insets(0, 0, 0, 0));
hideShowButton.setPreferredSize(new Dimension(16, 16));
hideShowButton.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
switchHideShow();
}
});
if (isHidden)
hideShowButton.setIcon(showIcon);
else
hideShowButton.setIcon(hideIcon);
hideShowBar.add(hideShowButton);
return hideShowBar;
}
/**
* 从外界接收消息<br/> 消息有消息头,这个类只接受MES_CHAT和MES_SERVER_NOTICE
*
* @param netMes
* 接受到的消息
*/
public void receiveMessage(NetMessage netMes) {
if (netMes == null || netMes.messageContent == null) {
if (Application.DEBUG > 6) {
System.out.println("哪里来的空消息:View.receiveMessage()");
}
return;
}
// 判断消息头
if (netMes.messageHeader.equals(NetMessage.MES_CHAT)) {// 如果是聊天内容
// 消息内容开始是用户名称
String text = (String) netMes.messageContent;
int index = text.indexOf(':');
if (index >= 0 && index < text.length() - 1) {
String senderUsername = text.substring(0, index);
String content = text.substring(index + 1, text.length());
showMenssage(content, senderUsername);// 显示消息
} else {
if (Application.DEBUG > 6)
System.out.println("聊天窗口接收到一个没有用户名称的消息");
}
} else if (netMes.messageHeader.equals(NetMessage.MES_SERVER_NOTICE)) {// 如果是服务器的通知
showServerNotice((String) netMes.messageContent);
} else {
if (Application.DEBUG > 6) {
System.out.println("聊天窗口接收到一个未知消息类型的消息");
}
}
}
/**
* 向外界发送消息,这个不具有消息头 会响应setname指令
*
* @param text
* 发送的消息的内容
*/
private void sendMessage(String text) {
if (text.toLowerCase().startsWith("/setname:")) {// 设置名称指令
int index = text.indexOf(':');
++index;
sendNewName(text.substring(index));
return;
} else {// 否则向外界发送消息
// 在这个窗口中显示内容
showMenssage(text);
// 向外界发送消息
sendMessage(NetMessage.MES_CHAT, text);
}
}
/**
* 核实新的名称是否有效<br/>
*
* @param newName
* 新的名称
* @return 当不含冒号且长度不小于1时返回真
*/
private boolean checkNewName(String newName) {
if (newName.length() < 1)
return false;
int index = newName.indexOf(':');
return index == -1;
}
/**
* 向外界发送新的名称 服务器会接受这个消息,并且更新名称映射表
*
* @param newName
*/
private void sendNewName(String newName) {
// check name:无冒号
System.out.println("newname:" + newName);
newName = newName.trim();
if (!checkNewName(newName))
return;
sendMessage(NetMessage.MES_SET_USER_NAME, newName);
showString("你已经将名称更改为" + newName + ",请等待服务器响应\r\n",
new Color(0xffcc00), true, true, 13);
}
/**
* 向外界发送消息
*
* @param mesHeader
* 发送的消息头
* @param mesContent
* 发送的消息内容
*/
private void sendMessage(String mesHeader, String mesContent) {
view.sendMessage(new NetMessage(mesHeader, mesContent));
}
/**
* 显示内容
*
* @param str
* 显示的字符串
* @param color
* 显示的颜色
* @param bold
* 是否为粗体
* @param italic
* 是否为斜体
* @param fontSize
* 字体的大小
*/
public void showString(String str, Color color, boolean bold,
boolean italic, int fontSize) {
SimpleAttributeSet attrSet = new SimpleAttributeSet();
StyleConstants.setForeground(attrSet, color);
StyleConstants.setBold(attrSet, bold);
StyleConstants.setItalic(attrSet, italic);
if (fontSize > 3) {
StyleConstants.setFontSize(attrSet, fontSize);
}
Document doc = chatContent.getDocument();
try {
doc.insertString(doc.getLength(), str, attrSet);
} catch (BadLocationException e) {
if (Application.DEBUG > 0)
System.out.println("插入字符失败: " + e);
}
// 将插入符置于末尾,这样才能够自动滚动
chatContent.setCaretPosition(chatContent.getDocument().getLength());
}
/**
* 显示内容的快捷方式 颜色是深灰色,常规字体样式,大小为14
*
* @param text
* 显示内容
*/
public void showString(String text) {
showString(text, Color.darkGray, false, false, 14);
}
// 上一次的消息所有者名称,如果相同则不再显示名称
private String lastMessageSenderName = null;
/**
* 本地显示消息的快捷方式 名称为“我”
*
* @param text
* 待显示的内容
*/
private void showMenssage(String text) {
showMenssage(text, "我");
}
/**
* 显示消息,会根据指定的用户名进行显示
*
*
* @param text
* 待显示的内容
* @param SenderName
* 发送者的名称
*/
public void showMenssage(String text, String SenderName) {
if (SenderName != null && !SenderName.equals(lastMessageSenderName)) {
lastMessageSenderName = SenderName;
showString(SenderName + "说 ", new Color(0x336600), true, false,
16);
showString("于 ", new Color(0x9999ff), false, false, 10);
showString(new SimpleDateFormat("MM月dd日 HH:mm:ss")
.format(new Date())
+ "\r\n", new Color(0x9999ff), false, true, 10);
}
// 显示解码后的文本,会有表情等
showUncodeText(text);
}
/**
* 显示服务器的消息
*
* @param text
* 待显示的内容
*/
public void showServerNotice(String text) {
showString("服务器通知:" + text + "\r\n", new Color(0xffcc00), true, true,
13);
lastMessageSenderName = null;
}
/**
* 显示经过转义之后的文本 表情的代表字符必须以‘/’开始, 要取消转义,在其之前加上'/’即可
*
* @param text
*/
private void showUncodeText(String text) {
int size = text.length();
int i = 0;
int lastIndex = 0;
while (i < size - 1) {
++i;
if (text.charAt(i - 1) == '/') { // 可能需要转义
if (text.charAt(i) != '/') { // 两个‘/’表示取消后面的转义
String uncodeStr;
if ((uncodeStr = getUncodeIndex(text.substring(i))) != null) {
showString(text.substring(lastIndex, i - 1));
showString(" ");// 这样连续两个icon不会重叠在一起
chatContent
.insertIcon(new ImageIcon(
getClass()
.getResource(
"faces/"
+ codeStr
.get(uncodeStr))));
showString(" ");
i += uncodeStr.length();
lastIndex = i;
}
} else {
showString(text.substring(lastIndex, i));
++i;
lastIndex = i;
}
}
}
if (lastIndex < size) {
showString(text.substring(lastIndex, size));
}
showString("\r\n");
}
/**
* 判断指定的字符串是否以一个转义字符开始 如果是,则返回转义字符的唯一索引值
*
* @param substring
* 指定的字符串
* @return 转义字符的唯一索引值,如果不是以转义字符开始,返回-1
*/
private String getUncodeIndex(String substring) {
Iterator<String> iter = codeStr.keySet().iterator();
while (iter.hasNext()) {
String str = iter.next();
if (substring.startsWith(str))
return str;
}
return null;
}
/**
* 内部类,输入Action 对Enter和Ctrl+Enter的处理就在这里
*
* @author Thihy
*
*/
public class ChatAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Send")) { // 发送文本,没有回车符
String text = chatField.getText();
if (text.equals(""))
return;
sendMessage(text);
chatField.setText("");
} else if (e.getActionCommand().equals("Enter")) {
chatField.append("\r\n");
} else if (e.getActionCommand().equals("Close")) {
dispose();
}
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -