import java.awt.Button;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.Point;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class GUI_Chat extends Frame {
private static final long serialVersionUID = 1L;
private TextArea viewTextArea = new TextArea(20, 1);
private TextArea sendTextArea = new TextArea();
private TextField ipTextField = new TextField(20);
private Button sendButton = new Button("发送");
private Button clearButton = new Button("清屏");
private Button logButton = new Button("记录");
private Button shakeButton = new Button("震动");
private Panel panel = new Panel();
private DatagramSocket socket;
private Lock lock = new ReentrantLock();
private FileWriter fw;
public GUI_Chat() throws Exception {
socket = new DatagramSocket(20000);
fw = new FileWriter("log.txt", true);
generateUI();
addListener();
new ReceiveThread().start(); // 开启接收数据的线程
}
private void generateUI() {
setTitle("GUI聊天室");
setSize(400, 600);
setLocation(600, 50);
setMinimumSize(new Dimension(400, 600)); // 设置最小尺寸
Font font = new Font("Courier New", Font.PLAIN, 15);
viewTextArea.setFont(font); // 设置字体
viewTextArea.setEditable(false); // 设置不可编辑(会改变背景色)
viewTextArea.setBackground(Color.WHITE); // 设置背景色
add(viewTextArea, BorderLayout.NORTH); // 把viewTextArea放在上面
sendTextArea.setFont(font);
add(sendTextArea, BorderLayout.CENTER); // 把sendTextArea放在中间
panel.add(ipTextField);
panel.add(sendButton);
panel.add(clearButton);
panel.add(logButton);
panel.add(shakeButton);
add(panel, BorderLayout.SOUTH); // 把Panel放在下面
}
private void addListener() {
addWindowListener(new WindowAdapter() { // 关闭窗体
public void windowClosing(WindowEvent e) {
try {
fw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
System.exit(0);
}
});
sendButton.addActionListener(new ActionListener() { // 发送功能
public void actionPerformed(ActionEvent e) {
send();
}
});
sendTextArea.addKeyListener(new KeyAdapter() { // 处理快捷键
public void keyPressed(KeyEvent e) {
if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_ENTER || e.isAltDown() && e.getKeyCode() == KeyEvent.VK_S) {
send();
e.consume(); // 取消当前事件
}
}
});
clearButton.addActionListener(new ActionListener() { // 清屏功能
public void actionPerformed(ActionEvent e) {
viewTextArea.setText("");
}
});
logButton.addActionListener(new ActionListener() { // 聊天记录
public void actionPerformed(ActionEvent e) {
showLog();
}
});
shakeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sendShake();
}
});
}
private void sendShake() {
try {
String ip = ipTextField.getText();
sendData(ip, new byte[] { -1 }); // 向执行IP发送一个特殊的消息
} catch (Exception e) {
e.printStackTrace();
}
}
private void showLog() {
try (Scanner scanner = new Scanner(new File("log.txt"));) {
viewTextArea.setText(""); // 清屏
fw.flush(); // 把未保存的数据写入文件
while (scanner.hasNextLine()) // 如过文件中有数据就进入循环
viewTextArea.append(scanner.nextLine() + "\\r\\n"); // 从文件读取一行, 追加到viewTextArea中
} catch (Exception e) {
e.printStackTrace();
}
}
private void send() {
try {
String msg = sendTextArea.getText(); // 获取要发的内容
String ip = ipTextField.getText(); // 获取目标地址
ip = ip.trim().length() == 0 ? "255.255.255.255" : ip;
String content = getTime() + " 我对 <" + (ip.equals("255.255.255.255") ? "所有人" : ip) + "> 说: \\r\\n" + msg + "\\r\\n\\r\\n";
lock.lock(); // 开始同步
sendData(ip, msg.getBytes()); // 发送数据
sendTextArea.setText(""); // 清空
viewTextArea.append(content); // 把要显示的内容追加到viewTextArea中
fw.write(content); // 保存聊天记录
lock.unlock(); // 结束同步
} catch (Exception e) {
e.printStackTrace();
}
}
private void sendData(String ip, byte[] data) throws Exception {
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName(ip), 20000);
socket.send(packet); // UDP发送数据
}
private String getTime() {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date); // 返回当前时间字符串
}
private class ReceiveThread extends Thread {
public void run() {
DatagramPacket packet = new DatagramPacket(new byte[8192], 8192);
while (true) {
try {
socket.receive(packet);
byte[] arr = packet.getData();
int length = packet.getLength();
String s = new String(arr, 0, length); // 接收到的字符串
String ip = packet.getAddress().getHostAddress();
String content = getTime() + " <" + ip + "> 对我说:\\r\\n" + s + "\\r\\n\\r\\n"; // 在viewTextArea中显示的内容
if (length == 1 && arr[0] == -1) { // 判断是否收到震动消息
doShake(); // 震动(改变位置)
continue; // 进入下一次循环
}
lock.lock(); // 开始同步
viewTextArea.append(content);
fw.write(content); // 保存聊天记录
lock.unlock(); // 结束同步
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void doShake() { //震动方法 (通过改变窗体的位置实现)
try {
Point point = getLocation();
for (int i = 0; i < 5; i++) {
Thread.sleep(30);
setLocation(point.x + 5, point.y);
Thread.sleep(30);
setLocation(point.x - 5, point.y + 5);
}
setLocation(point);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
new GUI_Chat().setVisible(true);
}
}