项目实例五 简易聊天系统

  • 一、前置知识
  • 二、AWT的基础使用
  • 三、实现cardPanel卡片布局----卡片切换
  • 四、聊天界面实现前置知识
  • 五、GuiChat简易聊天系统(一个成熟的聊天代码)
  • 六、WeiChat简易聊天系统(一个不成熟的聊天代码)

一、前置知识

  1. AWT与Swing
    AWT:抽象窗口工具包,重量级控件。
    Swing:AWT基础上,建立的一套图形界面系统,轻量级空间。(较之AWT界面更为美观)
    轻量级组件的缺点则是执行速度较慢,优点就是可以在所有平台上采用统一的行为。
  2. AWT事件监听:
    事件源(组件)、事件(Event)、监听器(Listener)、事件处理(引发事件后处理方式–处理代码)。
  3. public void windowOpened(windowEvent e):方法,窗口启动时自动执行。
    (注:可以实现弹出窗口,若要关闭窗口则关闭程序;可以实现启动时加载文字信息;可以实现弹出提示信息。)
  4. 流程布局将组件布置在定向流中,流向由容器的componentOrientation属性确定,可以是两个值之一:
    1. ComponentOrientation.LEFT_TO_RIGHT
    2. ComponentOrientation.RIGHT_TO_LEFT
  5. 流程布局通常用于布置面板中的按钮。 它可以水平排列按钮,直到不再有按钮在同一行上。 线对齐由align属性确定。 可能的值是:
    LEFT、RIGHT、CENTER、LEADING、TRAILING
  6. 监听器有:窗口监听器、键盘监听器、鼠标监听器、动作监听器。
  7. 五种布局管理器:
    1. FlowLayout、BorderLayout、GridLayout、GridBagLayout、CardLayout
    2. FlowLayout:基本布局,从左到右,从上至下。
    3. BorderLayout:边界布局管理器,东西南北中,五个方位。
    4. GridLayout:网格布局,划分区块大小相同。
    5. GridBagLayout:网格包布局,网格布局升级,划分区块大小可不一致。
    6. CardLayout:卡片布局。切换程序。(常用:卡片布局注册到监听器中,监听器绑定按钮,实现切换程序页面。)

二、AWT的基础使用

  1. package cn.itcast.test4;
    import java.awt.*;
    import java.awt.event.*;
    public class Javatest7 {/***@Title Javatest7.java*@description TODO*@time 2020-4-20 上午12:14:49*@author 羽落清泫*@version 1.0**/public static void main(String[] args) {//创建窗体对象final Frame f= new Frame("AWT窗口绘制"); //创建一个名为AWT窗口绘制的窗体//设置窗体属性// 设置窗体中的布局管理器为FlowLayout,所有组件左对齐,水平间距为20,垂直间距为30f.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 30));       //LEFT、RIGHT、CENTER、LEADING、TRAILING
    //      f.setTitle("关闭窗口");   //该窗口名设置,比Frame定义名优先级要高f.setSize(800,600);       //f.setSize(600,800);// 设置窗体大小f.setLocation(550,270);       //f.setLocation(550,270);// 设置窗体显示的位置
    //-------------------------------------------------     //适配器类,实现重写方法,点右上×,可关闭窗口//个人理解:一个窗口监听器,点击窗口关闭则关闭f.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){System.exit(0);}});TextField tf = new TextField(40);      //单行文本框,长度40f.add(tf);//一个键盘事件监听,tf.addKeyListener(new KeyAdapter() {public void keyPressed(KeyEvent e){  //键盘监听int code = e.getKeyCode();       //getKeyChar():处理的是比较高层的事件,返回的是每欠敲击键盘后得到的字符(中文输入法下就是汉字)。//getKeyCode()键盘上每一个按钮都有对应码(Code),可用来查知用户按了什么键//getKeyModifiersText()方法可返回修饰键的字符串,如返回“Shift”字符串或是“Ctrl+Shift”字符串, 不过你要先传入modifiers参数。if(!(code>=KeyEvent.VK_0 && code<=KeyEvent.VK_9)){ //VK_0 && VK_9:定义数据字符0~9System.out.println("必须是0-9数字");    //经测试有Bug:当中文输入法时,按出一串字符,以回车方式输入文本框中将不会提示[必须是0-9数字].}}});
    //-----------------------------------------------f.setVisible(true);        // 设置窗体可见}
    }
    

三、实现cardPanel卡片布局----卡片切换

  1. package cn.itcast.test4;
    import java.awt.*;
    import java.awt.event.*;
    public class Javatest8 {/***@Title Javatest8.java*@description 简易聊天系统--(swing体系)*@time 2020-4-20 下午2:53:14*@author 羽落清泫*@version 1.0**/public static void main(String[] args) {new Chat();}
    }
    //定义Chat继承Frame类,实现ActionListener接口
    class Chat extends Frame implements ActionListener {private static final long serialVersionUID = 1L;Button but1,but2;      //定义两个按钮,实现点击按钮,面板切换Panel p1=new Panel();    //p1面板:实现页面内容展示Panel p2=new Panel();    //p2面板,封装按钮,实现展示按钮CardLayout cardLayout = new CardLayout();// 定义卡片布局对象public Chat(){         // 定义构造方法,设置(卡片布局管理器的)属性setTitle("简易聊天系统--(Swing体系)"); //设置窗口名setSize(800, 600);   // 设置窗体大小setLocation(550,270);  //f.setLocation(550,270);// 设置窗体显示的位置setVisible(true);// 为窗口添加关闭事件监听器this.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e) {Chat.this.dispose();}});p1.setLayout(cardLayout);        // 调用定义的布局对象,设置p1面板对象为卡片布局
    //-------------------------------------------------------// 在cardPanel面板对象中添加2个文本标签Label b1=new Label("第一个界面", Label.CENTER);p1.add(b1);// 创建两个按钮对象but1 = new Button("第一张卡片");but2 = new Button("第二张卡片");//按钮对象绑定动作监听器but1.addActionListener(this);but2.addActionListener(this);//按钮绑定面板p2.add(but1);p2.add(but2);// 将p1面板放置在窗口边界布局的中间,窗口默认为边界布局this.add(p1, BorderLayout.CENTER);// 将p2面板放置在窗口边界布局的北区,this.add(p2, BorderLayout.NORTH); //NORTH、SOUTH、CUNTER、EAST、WEAST}
    //------------------------------------------//实现按钮的监听触发,并对触发事件做出相应的处理(动作监听器)public void actionPerformed(ActionEvent e) {//      // 切换p1面板中当前组件后一个面板,若当前面板为最后一个组件,则显示第一个面板。
    //      if(e.getSource()==but1){//          cardLayout.next(p1);
    //      }
    //       切换p1面板中当前组件前一个面板
    //      if(e.getSource()==but2){//          cardLayout.previous(p1);
    //      }if(e.getSource()==but1){cardLayout.first(p1);this.setTitle("聊天页");}if(e.getSource()==but2){cardLayout.last(p1);this.setTitle("展示信息页");}}
    //------------------------------------------
    }
    

四、聊天界面实现前置知识

  1. . 网络模型:OSI(开放系统互连)
  2. IP地址:网络中设备的地址(数字或主机名)(InetAddress)
  3. 端口号:用于表示进程的逻辑地址,有效端口:0-65535。(0-1024为系统保留端口。)
  4. UDP:不建立连接,不可靠协议,速度快。
    TCP:建立连接,形成传输数据的通道。
  5. UDP方法:
    1. DatagramPacket:UDP发送端方法。
    2. DatagramSocket:UDP接收端方法。

五、GuiChat简易聊天系统(一个成熟的聊天代码)

  1. package cn.test;
    import java.io.*;
    import java.net.*;
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    public class GuiChat extends JFrame{private static final long serialVersionUID = 1L;   赋予一个UID,消除GuiChat黄色警告private static final int DEFAULT_PORT = 8899;// 把主窗口分为NORTH、CEMTER和SOUTH三个部分private JLabel stateLB; // 显示监听状态private JTextArea centerTextArea;       // 显示聊天记录private JPanel southPanel;               // 最下面的面板private JTextArea inputTextArea;        // 聊天输入框private JPanel bottomPanel;              // 放置 IP输入框,按钮等private JTextField ipTextField;         // IP输入框private JTextField remotePortTF;        // 端口号输入框private JButton sendBT;                   // 发送按钮private JButton clearBT;                  // 清除聊天记录按钮private DatagramSocket datagramSocket; // 用于后面功能的实现private void setUpUI() {               // 初始化Swing窗口// 初始化窗口setTitle("GUI聊天");setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setSize(400, 400);             // 设置窗口的大小setResizable(false);           // 窗口大小不可调整setLocationRelativeTo(null); // 窗口居中// 窗口的NORTH部分stateLB = new JLabel("当前还未启动监听");stateLB.setHorizontalAlignment(JLabel.RIGHT);// 窗口的CENTER部分centerTextArea = new JTextArea();     // 聊天记录显示区域centerTextArea.setEditable(false);centerTextArea.setBackground(new Color(211, 211, 211));// 窗口的SOUTH部分southPanel = new JPanel(new BorderLayout());inputTextArea = new JTextArea(5, 20);// 内容输入区域bottomPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5));ipTextField = new JTextField("127.0.0.1", 8);remotePortTF = new JTextField(String.valueOf(DEFAULT_PORT), 3);sendBT = new JButton("发送");clearBT = new JButton("清屏");bottomPanel.add(ipTextField);bottomPanel.add(remotePortTF);bottomPanel.add(sendBT);bottomPanel.add(clearBT);southPanel.add(new JScrollPane(inputTextArea), BorderLayout.CENTER);southPanel.add(bottomPanel, BorderLayout.SOUTH);// 添加窗口NORTH、CENTER、SOUTH部分的组件add(stateLB, BorderLayout.NORTH);add(new JScrollPane(centerTextArea), BorderLayout.CENTER);add(southPanel, BorderLayout.SOUTH);setVisible(true);}private void setListener() {// 为sendBT按钮添加事件监听器sendBT.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {// 获取发送的目标ip地址和端口号final String ipAddress = ipTextField.getText();final String remotePort = remotePortTF.getText();// 判断ip地址和端口号是否为空if (ipAddress == null || ipAddress.trim().equals("")|| remotePort == null || remotePort.trim().equals("")) {JOptionPane.showMessageDialog(GuiChat.this, "请输入IP地址和端口号");return;}if (datagramSocket == null || datagramSocket.isClosed()) {JOptionPane.showMessageDialog(GuiChat.this, "监听不成功");return;}// 获得需要发送的内容String sendContent = inputTextArea.getText();byte[] buf = sendContent.getBytes();try {// 将发送的内容显示在自己的聊天记录中centerTextArea.append("我对 " + ipAddress + ":" + remotePort + " 说:\n" + inputTextArea.getText() + "\n\n");// 添加内容后,使滚动条自动滚动到最底端centerTextArea.setCaretPosition(centerTextArea.getText().length());// 发送数据datagramSocket.send(new DatagramPacket(buf, buf.length,InetAddress.getByName(ipAddress), Integer.parseInt(remotePort)));inputTextArea.setText("");} catch (IOException e1) {JOptionPane.showMessageDialog(GuiChat.this, "出错了,发送不成功");e1.printStackTrace();}};});// 为clearBT按钮添加事件监听器clearBT.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {centerTextArea.setText(""); // 清空聊天记录的内容}});}private void initSocket() {int port = DEFAULT_PORT;while (true) {try {if (datagramSocket != null && !datagramSocket.isClosed()) {datagramSocket.close();}try { // 判断端口号是否在1-65535之间port = Integer.parseInt(JOptionPane.showInputDialog(this,"请输入端口号", "端口号", JOptionPane.QUESTION_MESSAGE));if (port < 1 || port > 65535) {throw new RuntimeException("端口号超出范围");}} catch (Exception e) {JOptionPane.showMessageDialog(null,"你输入的端口不正确,请输入1-65535之间的数");continue; // 端口不正确重新填写}// 启动DatagramSocketInetAddress localAddress = null;datagramSocket = new DatagramSocket(port);startListen(); // 调用startListen方法// 在stateLB中显示程序监听的端口号try {localAddress = InetAddress.getLocalHost();} catch (UnknownHostException e) {// TODO Auto-generated catch blocke.printStackTrace();}stateLB.setText("本机IP为:"+localAddress.getHostAddress()+","+"已在 " + port + " 端口监听");break;} catch (SocketException e) { // 端口号被占用重新填写JOptionPane.showMessageDialog(this, "端口已被占用,请重新设置端口");stateLB.setText("当前还未启动监听");}}}private void startListen() {new Thread() {private DatagramPacket p;public void run() {byte[] buf = new byte[1024];// 创建DatagramPacketp = new DatagramPacket(buf, buf.length);while (!datagramSocket.isClosed()) {try {datagramSocket.receive(p); // 接收聊天消息// 添加到聊天记录框centerTextArea.append(p.getAddress().getHostAddress()+ ":"+ ((InetSocketAddress) p.getSocketAddress()).getPort() + " 对我说:\n"+ new String(p.getData(), 0, p.getLength())+ "\n\n");// 使滚动条自动滚动到最底端centerTextArea.setCaretPosition(centerTextArea.getText().length());} catch (IOException e) {e.printStackTrace();}}}}.start();}public GuiChat() {setUpUI();initSocket();setListener();}public static void main(String[] args) {new GuiChat();}
    }
    

六、WeiChat简易聊天系统(一个不成熟的聊天代码)

  1. package cn.itcast.test4;
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;import java.io.*;
    import java.net.*;
    public class Javatest9 {/***@Title Javatest9.java*@description 简易聊天系统*@time 2020-4-20 下午5:03:54*@author 羽落清泫*@version 1.0**/public static void main(String[] args) {// 实例化Chat,调用new WeiChat();new ServerDemo();    }
    }
    class WeiChat{//    private static final long serialVersionUID = 1L;//赋予一个UID,消除Chat黄色警告private DatagramSocket datagramSocket; // 用于后面功能的实现private JTextArea jt1;public WeiChat(){JFrame f = new JFrame("简易聊天系统--发送端");
    //    f.setTitle("Swing体系--简易聊天系统");f.setSize(400, 600);  // 设置窗体大小f.setLocation(750,270);    //f.setLocation(550,270);// 设置窗体显示的位置
    //    setResizable(false);           // 窗口大小不可调整
    //    setLocationRelativeTo(null); // 窗口居中
    //    // 设置流式布局管理器
    //    f.setLayout(new FlowLayout());
    //--------------------------------------//创建菜单项JMenuBar menuBar = new JMenuBar(); // 创建菜单栏f.setJMenuBar(menuBar);            // 将菜单栏添加到JFrame窗口中JMenu menu = new JMenu("操作");     // 创建菜单JMenu menu2 = new JMenu("测试1");   // 创建菜单menuBar.add(menu);                 // 将菜单添加到菜单栏上menuBar.add(menu2);// 创建菜单项JMenuItem item1 = new JMenuItem("弹出窗口");JMenuItem item2 = new JMenuItem("关闭");JMenuItem item3 = new JMenuItem("模态对话框");JMenuItem item4 = new JMenuItem("非模态对话框");menu.add(item1);menu.add(item2);menu2.add(item3);menu2.addSeparator();  // 添加一个分隔符menu2.add(item4);
    //--------------------------------------//-------------窗口NORTH---------------------JLabel jl1=new JLabel("标签");  // 显示监听状态jl1.setHorizontalAlignment(JLabel.RIGHT);  //文本设置左右对齐方法f.add(jl1,BorderLayout.NORTH);//-------------窗口CONTER--------------------jt1 = new JTextArea("在此处显示聊天内容:",80, 20);jt1.setEditable(false); //设置文本域只显示,不可编辑jt1.setMargin(new Insets(5, 5, 5, 5));    //设置文本(聊天信息)在文本域内显示的边距jt1.setBackground(new Color(222, 222, 222));  //颜色最大值(255,255,255)jt1.setSize(100,300);f.add(new JScrollPane(jt1), BorderLayout.CENTER);//-------------窗口SOUTH-----------JPanel jl0=new JPanel(new BorderLayout());  //用于装所有组件,放到窗口南部JPanel jl2 = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5));   //按流式布局装入按钮等组件final JTextArea  jt2=new JTextArea(5, 10);           //用于文本输入
    //    jt2.setPreferredSize(new Dimension (30,30));          //final JTextField ipTextF = new JTextField("192.168.151.161", 8);       //设置默认IP地址为:"127.0.0.1"final JTextField remotePort = new JTextField(String.valueOf("8859"), 3); //设置默认端口号为:8859JButton but11 = new JButton("发送");jl2.add(ipTextF);jl2.add(remotePort);jl2.add(but11);jl0.add(new JScrollPane(jt2),BorderLayout.CENTER);jl0.add(jl2,BorderLayout.SOUTH);f.add(jl0,BorderLayout.SOUTH);//遇到的问题,当窗口长宽过大时,窗口内文本(域)标签会自适应窗口,无法设置文本标签与窗口之间的(间隔)留白--边框间距问题---------//试解决方案1:设置空的标签组件,设置BorderLayout布局为EAST、WEST,是否解决(待定)//试解决方案2:设置其它面板,按方案一的方法,试解决边框间距问题(待定)// 设置点击关闭按钮时的默认操作f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);
    //----------------------------------------
    //--------------------------------------final JLabel label = new JLabel();final JDialog dialog = new JDialog(f, "Dialog"); // 定义一个JDialog对话框final JButton btn3 = new JButton("确定");          // 创建按钮对象dialog.add(btn3); // 在对话框的内容面板添加按钮// 为"模态对话框"按钮添加点击事件item3.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {dialog.setSize(220, 150);                         // 设置对话框大小dialog.setLocation(830, 500);                     // 设置对话框位置dialog.setLayout(new FlowLayout());               // 设置布局管理器// 设置对话框为模态dialog.setModal(true);// 如果JDialog窗口中没有添加了JLabel标签,就把JLabel标签加上if (dialog.getComponents().length == 1) {dialog.add(label);}// 否则修改标签的内容label.setText("模式对话框,点击确定按钮关闭");// 显示对话框dialog.setVisible(true);}});// 为"模态对话框"按钮添加点击事件item4.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {dialog.setSize(220, 150);                         // 设置对话框大小dialog.setLocation(830, 500);                     // 设置对话框位置dialog.setLayout(new FlowLayout());               // 设置布局管理器// 设置对话框为非模态dialog.setModal(false);// 如果JDialog窗口中没有添加了JLabel标签,就把JLabel标签加上if (dialog.getComponents().length == 1) {dialog.add(label);}// 否则修改标签的内容label.setText("模式对话框,点击确定按钮关闭");// 显示对话框dialog.setVisible(true);}});// 为对话框中的按钮添加点击事件btn3.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {dialog.dispose();}});//item1功能:弹出窗口item1.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {// 创建一个JDialog窗口dialog.setTitle("弹出窗口,测试使用...");dialog.setSize(400, 200);dialog.setLocation(730, 500);dialog.setVisible(true);}});//item2功能:关闭窗体item2.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {System.exit(0);}});
    //---------------------------------------//消息发出与接收代码//1. 消息发出but11.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){//获取信息发送的ip地址与端口String ipAddress=ipTextF.getText();String remote=remotePort.getText();//一般来说此处需要验证ip地址与端口号是否正确try {//获得发送的内容String sendConent =jt2.getText();//获得内容放入一个数组中—————转换成ByteBuf后发送 byte[]byte[] buf=sendConent.getBytes();   //byte[] buf=sendConent.getBytes("UTF-8");//将信息展示到自己的聊天界面jt1.append("\n"+"本地"+ipAddress+":"+remote+"说:"+"  "+jt2.getText()+"\n");//发送数据-------datagramSocket方法需要抛出异常datagramSocket=new DatagramSocket();datagramSocket.send(new DatagramPacket(buf, buf.length,InetAddress.getByName(ipAddress), Integer.parseInt(remote)));//datagramSocket.send(new DatagramPacket(buf, buf.length,InetAddress.getByName(ipAddress), Integer.parseInt(remotePort)));//设置发送后,输入框清空jt2.setText(null);//------------以下为异常抛出,原因暂不知-----------} catch (SocketException e1) {// TODO Auto-generated catch blocke1.printStackTrace();} catch (NumberFormatException e1) {// TODO Auto-generated catch blocke1.printStackTrace();} catch (UnknownHostException e1) {// TODO Auto-generated catch blocke1.printStackTrace();} catch (IOException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}}});//2. 设置监听的端口,默认为8859int port = 8859;while(true){try{ // 判断端口号是否在1-65535之间port = Integer.parseInt(JOptionPane.showInputDialog(f,"请输入端口号","端口号",JOptionPane.QUESTION_MESSAGE));if (port < 1 || port > 65535) {        //JOptionPane:可以轻松地弹出一个标准对话框,提示用户获取值或通知他们某些东西 //QUESTION_MESSAGE:用于问题。 throw new RuntimeException("端口号超出范围");}}catch(Exception e){JOptionPane.showMessageDialog(null,"你输入的端口不正确,请输入1-65535之间的数");continue; // 端口不正确重新填写}//启动DatagramSocketInetAddress localAddress = null; //表示Internet协议(IP)地址。try {datagramSocket = new DatagramSocket(port);startChat();} catch (SocketException e2) {// TODO Auto-generated catch blocke2.printStackTrace();}try {localAddress = InetAddress.getLocalHost();   //获取本机IP地址} catch (UnknownHostException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}jl1.setText("本机IP为:"+localAddress.getHostAddress()+","+"已在 " + port + " 端口监听");break;}
    }
    //-----------------------------------------//3. 定义一个接收消息的方法,可被调用private void startChat(){new Thread(){private DatagramPacket p;//DatagramPacket:实现无连接分组传送服务,每个消息从一台机器路由到另一台机器public void run(){byte[] buf=new byte[1024];//调用DatagramPacket,并与数组绑定p=new DatagramPacket(buf,buf.length);while(!datagramSocket.isClosed()){try {datagramSocket.receive(p); // 接收聊天消息// 添加到聊天记录框jt1.append(p.getAddress().getHostAddress()+ ":"+ ((InetSocketAddress) p.getSocketAddress()).getPort() + " 对我说:\n"+ new String(p.getData(), 0, p.getLength())+ "\n\n");// 使滚动条自动滚动到最底端jt1.setCaretPosition(jt1.getText().length());} catch (IOException e) {e.printStackTrace();}}}}.start();};
    }
    class ServerDemo {//    private static final long serialVersionUID = 1L;//赋予一个UID,消除Chat黄色警告private DatagramSocket datagramSocket; // 用于后面功能的实现private JTextArea jt1;public ServerDemo(){JFrame f = new JFrame("简易聊天系统--发送端");
    //    f.setTitle("Swing体系--简易聊天系统");f.setSize(400, 600);  // 设置窗体大小f.setLocation(250,270);    //f.setLocation(550,270);// 设置窗体显示的位置
    //    setResizable(false);           // 窗口大小不可调整
    //    setLocationRelativeTo(null); // 窗口居中
    //    // 设置流式布局管理器
    //    f.setLayout(new FlowLayout());
    //--------------------------------------//创建菜单项JMenuBar menuBar = new JMenuBar(); // 创建菜单栏f.setJMenuBar(menuBar);            // 将菜单栏添加到JFrame窗口中JMenu menu = new JMenu("操作");     // 创建菜单JMenu menu2 = new JMenu("测试1");   // 创建菜单menuBar.add(menu);                 // 将菜单添加到菜单栏上menuBar.add(menu2);// 创建菜单项JMenuItem item1 = new JMenuItem("弹出窗口");JMenuItem item2 = new JMenuItem("关闭");JMenuItem item3 = new JMenuItem("模态对话框");JMenuItem item4 = new JMenuItem("非模态对话框");menu.add(item1);menu.add(item2);menu2.add(item3);menu2.addSeparator();  // 添加一个分隔符menu2.add(item4);
    //--------------------------------------//-------------窗口NORTH---------------------JLabel jl1=new JLabel("标签");  // 显示监听状态jl1.setHorizontalAlignment(JLabel.RIGHT);  //文本设置左右对齐方法f.add(jl1,BorderLayout.NORTH);//-------------窗口CONTER--------------------jt1 = new JTextArea("在此处显示聊天内容:",80, 20);jt1.setEditable(false); //设置文本域只显示,不可编辑jt1.setMargin(new Insets(5, 5, 5, 5));    //设置文本(聊天信息)在文本域内显示的边距jt1.setBackground(new Color(222, 222, 222));  //颜色最大值(255,255,255)jt1.setSize(100,300);f.add(new JScrollPane(jt1), BorderLayout.CENTER);//-------------窗口SOUTH-----------JPanel jl0=new JPanel(new BorderLayout());  //用于装所有组件,放到窗口南部JPanel jl2 = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5));   //按流式布局装入按钮等组件final JTextArea  jt2=new JTextArea(5, 10);           //用于文本输入
    //    jt2.setPreferredSize(new Dimension (30,30));          //final JTextField ipTextF = new JTextField("192.168.151.161", 8);       //设置默认IP地址为:"127.0.0.1"final JTextField remotePort = new JTextField(String.valueOf("11111"), 3);    //设置默认端口号为:11111JButton but11 = new JButton("发送");jl2.add(ipTextF);jl2.add(remotePort);jl2.add(but11);jl0.add(new JScrollPane(jt2),BorderLayout.CENTER);jl0.add(jl2,BorderLayout.SOUTH);f.add(jl0,BorderLayout.SOUTH);//遇到的问题,当窗口长宽过大时,窗口内文本(域)标签会自适应窗口,无法设置文本标签与窗口之间的(间隔)留白--边框间距问题---------//试解决方案1:设置空的标签组件,设置BorderLayout布局为EAST、WEST,是否解决(待定)//试解决方案2:设置其它面板,按方案一的方法,试解决边框间距问题(待定)// 设置点击关闭按钮时的默认操作f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);
    //----------------------------------------
    //--------------------------------------
    //      final JLabel label = new JLabel();final JDialog dialog = new JDialog(f, "Dialog"); // 定义一个JDialog对话框final JButton btn3 = new JButton("确定");          // 创建按钮对象dialog.add(btn3); // 在对话框的内容面板添加按钮//item2功能:关闭窗体item2.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {System.exit(0);}});
    //---------------------------------------//消息发出与接收代码//1. 消息发出but11.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){//获取信息发送的ip地址与端口String ipAddress=ipTextF.getText();String remote=remotePort.getText();//一般来说此处需要验证ip地址与端口号是否正确try {//获得发送的内容String sendConent =jt2.getText();//获得内容放入一个数组中—————转换成ByteBuf后发送 byte[]byte[] buf=sendConent.getBytes();   //byte[] buf=sendConent.getBytes("UTF-8");//将信息展示到自己的聊天界面jt1.append("\n"+"本地"+ipAddress+":"+remote+"说:"+"  "+jt2.getText()+"\n");//发送数据-------datagramSocket方法需要抛出异常datagramSocket=new DatagramSocket();datagramSocket.send(new DatagramPacket(buf, buf.length,InetAddress.getByName(ipAddress), Integer.parseInt(remote)));//datagramSocket.send(new DatagramPacket(buf, buf.length,InetAddress.getByName(ipAddress), Integer.parseInt(remotePort)));//设置发送后,输入框清空jt2.setText(null);//------------以下为异常抛出,原因暂不知-----------} catch (SocketException e1) {// TODO Auto-generated catch blocke1.printStackTrace();} catch (NumberFormatException e1) {// TODO Auto-generated catch blocke1.printStackTrace();} catch (UnknownHostException e1) {// TODO Auto-generated catch blocke1.printStackTrace();} catch (IOException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}}});//2. 设置监听的端口,默认为11111int port = 11111;while(true){try{ // 判断端口号是否在1-65535之间port = Integer.parseInt(JOptionPane.showInputDialog(f,"请输入端口号","端口号",JOptionPane.QUESTION_MESSAGE));if (port < 1 || port > 65535) {      //JOptionPane:可以轻松地弹出一个标准对话框,提示用户获取值或通知他们某些东西 //QUESTION_MESSAGE:用于问题。 throw new RuntimeException("端口号超出范围");}}catch(Exception e){JOptionPane.showMessageDialog(null,"你输入的端口不正确,请输入1-65535之间的数");continue; // 端口不正确重新填写}//启动DatagramSocketInetAddress localAddress = null; //表示Internet协议(IP)地址。try {datagramSocket = new DatagramSocket(port);} catch (SocketException e2) {// TODO Auto-generated catch blocke2.printStackTrace();}startChat();try {localAddress = InetAddress.getLocalHost();   //获取本机IP地址} catch (UnknownHostException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}jl1.setText("本机IP为:"+localAddress.getHostAddress()+","+"已在 " + port + " 端口监听");break;}
    }
    //-----------------------------------------//3. 定义一个接收消息的方法,可被调用private void startChat(){new Thread(){private DatagramPacket p;//DatagramPacket:实现无连接分组传送服务,每个消息从一台机器路由到另一台机器public void run(){byte[] buf=new byte[1024];//调用DatagramPacket,并与数组绑定p=new DatagramPacket(buf,buf.length);while(!datagramSocket.isClosed()){try {datagramSocket.receive(p); // 接收聊天消息// 添加到聊天记录框jt1.append(p.getAddress().getHostAddress()+ ":"+ ((InetSocketAddress) p.getSocketAddress()).getPort() + " 对我说:\n"+ new String(p.getData(), 0, p.getLength())+ "\n\n");// 使滚动条自动滚动到最底端jt1.setCaretPosition(jt1.getText().length());} catch (IOException e) {e.printStackTrace();}}}}.start();};
    }
    //--------------其他测试:可删除-----------------------------
    //public class test6 {//  /**
    //   *@Title test6.java
    //   *@description TODO
    //   *@time 2020-4-20 下午9:07:57
    //   *@author 羽落清泫
    //   *@version 1.0
    //   * @throws IOException
    //   **/
    //  public static void main(String[] args) throws IOException {//      // TODO Auto-generated method stub
    //      InetAddress localAddress = InetAddress.getLocalHost();
    //      InetAddress remoteAddress = InetAddress.getByName("www.baidu.com");
    //      System.out.println("本机的IP地址:" + localAddress.getHostAddress());
    //      System.out.println("itcast的IP地址:" + remoteAddress.getHostAddress());
    //      System.out.println("itcast的主机名为:" + remoteAddress.getHostName());
    //  }
    //}
    //该代码BUG待更新中......emmmm......
    

End.

Java基础 项目实例五 简易聊天系统相关推荐

  1. Java校招笔试题-Java基础部分(五)

    导语   Java 面试题集2021版 Java基础部分五 47.String 和String Buffer的区别 48.StringBuffer和StringBuilder的区别 49.数组有没有l ...

  2. Java实习生常规技术面试题每日十题Java基础(五)

    目录 1.启动一个线程是用run()还是start()? . 2.线程的基本状态以及状态之间的关系. 3.Set和List的区别,List和Map的区别? 4.同步方法.同步代码块区别? 5.描述Ja ...

  3. Java基础系列五 面向对象程序设计

    Java基础系列第一章 初识Java语言 Java基础系列第二章 Java语言基础 Java基础系列第三章 Java流程控制 Java基础系列第四章 Java数组与字符串 Java基础系列第五章 面向 ...

  4. Java基础知识(五) 字符串与数组

    Java基础知识 字符串与数组 1. 字符串的创建与存储的机制是什么 2. "==".equals和hashCode有什么区别 3. String.StringBuffer.Str ...

  5. Java基础(五)-类的特性

    文章首发及后续更新:https://mwhls.top/1277.html 新的更新内容请到mwhls.top查看. 无图/无目录/格式错误/更多相关请到上方的文章首发页面查看. Java基础目录 这 ...

  6. java基础知识五、六、七、八

    第五章 在较大的数之前先增加较小数是减小误差的一种方法. 第六章 方法签名(Method Signature)指方法名称.参数类型和参数数量.java编译器根据方法签名决定使用哪个方法. 调用方法时, ...

  7. Java基础 第五节 第七课

    Stream 流 概述 传统集合的多步遍历代码 循环遍历的弊端 Stream 的更优写法 流式思想概述 步骤方案 元素队列 获取流 根据 Collection 获取流 根据 Map 获取流 根据数组获 ...

  8. Java基础 第五节 第九课

    方法引用 概述 冗余的 Lambda 场景 用方法改进代码 问题分析 代码展示 方法引用符 语义分析 推导与省略 通过对象名成员方法 通过类名称引用静态方法 通过 super 引用成员方法 通过 th ...

  9. Java基础系列五之API

    对象数组 定义:用来存储对象的数组 Student[] student = new Student[5] ; Arrays:针对数组操作的工具类  (提供了针对数组排序的方法和二分搜索法) publi ...

最新文章

  1. (JavaWeb)ServletContext对象
  2. Windows Mobile 6 中为开发人员提供的新功能(1)
  3. JAVA——Windows双击jar包为可执行操作
  4. [导入]PHP通用分页类
  5. scrapy架构解析
  6. 是什么样的骚操作让应用上线节省90%的时间 1
  7. Java基础篇2——运算符
  8. 【OpenCV】OpenCV函数精讲之 -- 颜色空间缩减及LUT函数
  9. 交换机的RTU License 华为S5700交换机
  10. Zabbix5 安装教程
  11. ubuntu-创建桌面快捷方式
  12. Python函数combination
  13. UEditor自定义表情包
  14. DDIM代码详细解读(1):数据集加载、类别条件信息读取、关键超参数解析
  15. 华为交换机5720常用命令
  16. 【Python】在Anaconda中设置清华镜像站并解决“http error“问题
  17. 让工作快乐起来500强企业推崇的新理念
  18. 经典黑白搭配 现代简约风格设计美学精神
  19. Cadence OrCAD Capture 如何批量替换元器件
  20. 队列BlockingQueue

热门文章

  1. onfouce事件与onblur事件
  2. 扩展卡尔曼滤波的理解与对加入高斯噪声的正弦信号进行滤波实例
  3. 国内外遥感类核心期刊汇总
  4. 自己实现notifyDatasetChanged
  5. 强化学习:Q-Learning
  6. 10章 RxJava源码分析
  7. 股票公式成功率测试软件,超牛指标,成功率95%(同花顺公式 副图 源码 测试图)...
  8. 威联通nas怎么更换大硬盘_扩充存储池:威联通NAS添加硬盘的扩容设置教程
  9. 【电源专题】什么是开关稳压器
  10. 71 ----二次曲面方程:椭球面、双曲面、抛物面、二次曲面的种类