admin管理员组

文章数量:1588845

1.产品展示

1.1这是我们的登录界面

2.细节!!!talk is cheap,show me the code

• UI设计
• Socket网络编程
• 多线程与线程通讯

2.1主页面

2.1.1注册表记住密码

/**
 * 注册表实现记住密码,注册表在我们的电脑里面,用键值对的方式存取
*/
	Preferences pre = Preferences.userNodeForPackage(getClass());
	// 注册表实现记住密码
	pre = Preferences.userNodeForPackage(UserLoginUI.class);
	pre.put(text_uname.getText(), text_pwd.getText());//存放密码
	pre.get(text_uname.getText(), null)//根据键,获取密码


2.1.2界面布局

  • 首先,我们采用的SWT编程,SWT的窗口会自动提供一个title框,这个title框可玩性太小,不能设置样式图层等,所以我们选择去掉title框
//自定义拖拽事件开始 
    private int startX;// 记录鼠标移动前的X坐标
    private int startY;// 记录鼠标移动前的y坐标
    private int lastX;// 记录鼠标移动后的x坐标
    private int lastY;// 记录鼠标移动后的y坐标
    private boolean isdown;// 记录鼠标是否按下
    UserMainUI thisOBJ = this;// 实现窗口拖拉 自己创建自己这个类
        /**
         * 设置指定区域label_topimage窗口移动事件,你要在那个组件上添加拖拽事件就在那里添加,我这里是在label里添加的
         */
        label_topimage.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                thisOBJ.isdown = true;
                thisOBJ.startX = e.x;
                thisOBJ.startY = e.y;
            }
            @Override
            public void mouseUp(MouseEvent e) {
                thisOBJ.isdown = false;
                super.mouseUp(e);
            }
        });
        label_topimage.addMouseMoveListener(new MouseMoveListener() {
            @Override
            public void mouseMove(MouseEvent arg0) {
                if (thisOBJ.isdown) {
                    thisOBJ.lastX = arg0.x;
                    thisOBJ.lastY = arg0.y;
                    int x = thisOBJ.lastX - thisOBJ.startX;
                    int y = thisOBJ.lastY - thisOBJ.startY;
                    org.eclipse.swt.graphics.Point location = shell.getLocation();
                    int xx = location.x;
                    int yy = location.y;
                    shell.setLocation(x + xx, y + yy);
                }
            }
        });
  • 添加自定义的按钮,这里我的思路是直接在label里面贴图,然后设置label的MouseEnter、MouseExit、MouseDown事件,鼠标进入和离开label时通过更改label里贴的图造成按钮的感觉,通过MouseDown添加最小化shell.setMinimized(true)和做大化shell.setMaximized(true)以及销毁shell.dispose。
  • 这里关键的是当多个Composite面板和label框堆叠在一起时候的显示问题,我们的做法是把背景图的label放在最下面一层,这样就能呈现比较好的效果了

2.1.3用户主页采用的堆栈式布局StackLayout
主页我们采用的是堆栈式布局,这样就能配合自定义的label按钮的点击事件呈现切换界面的效果
SWT里的Composite面板为我们提供了六种布局方式,分别是

  • Absolute layout绝对布局
  • GridLayout网格式布局
  • FillLayout填充式布局
  • RowLayout行式布局
  • FormLayout从布局
  • StackLayout堆栈式布局

StackLayout堆栈式布局,他是所有的子面板都在堆叠在父面板上,可以通过父面板选在在最上面的那块,但只能看到堆在最上面的那块,可以设计触发事件改变最上面的面板达到切换界面的效果

private StackLayout stackLayoutne;// 堆栈式布局
// 采用堆栈式布局
        stackLayoutne = new StackLayout();
        composite_down.setLayout(stackLayoutne);
        // 将备选的三块面板层叠放到composite_down主面板下
        composite_tree = new Composite_tree(composite_down, SWT.NONE, fromQQ);
        composite_message = new Composite_message(composite_down, SWT.NONE, fromQQ);
        composite_weChat = new Composite_weChat(composite_down, SWT.NONE);
        stackLayoutne.topControl = composite_message;// 将composite_message聊天面板设置为主面板
        FormData fd_composite_down = new FormData();
        fd_composite_down.bottom = new FormAttachment(0, 673);
        fd_composite_down.right = new FormAttachment(0, 379);
        fd_composite_down.top = new FormAttachment(0, 222);
        fd_composite_down.left = new FormAttachment(0);
        composite_down.setLayoutData(fd_composite_down);
        composite_down.setBackground(SWTResourceManager.getColor(SWT.COLOR_WHITE));
//事件里面切换面板
            label_center_center.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                lblNewLabel_center_left.setImage(SWTResourceManager.getImage(UserMainUI.class, "/images/消息_不亮.png"));
                label_center_center.setImage(SWTResourceManager.getImage(UserMainUI.class, "/images/联系人_亮.png"));
                label_center_right.setImage(SWTResourceManager.getImage(UserMainUI.class, "/images/空间_不亮.png"));
                stackLayoutne.topControl = composite_tree;// 将composite_message聊天面板设置为主面板
                composite_down.layout();// 重置布局,刷新界面
            }
        });

2.1.4用户主界面里的子面板采用SWT自带的滑动面板ScrolledComposite
这样就可以在以面板为父类的组件增长时自动产生滚动条,如果嫌ScrolledComposite太丑不好看的话,可以自己写一个子类继承ScrolledComposite,重写里面的方法

//设置滚动面板    
        ScrolledComposite scrolledComposite = new ScrolledComposite(this, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
        scrolledComposite.setBounds(0, 0, 380, 496);
        scrolledComposite.setExpandHorizontal(true);
        scrolledComposite.setExpandVertical(true);
        Composite composite_1 = new Composite(scrolledComposite, SWT.NONE);
        composite_1.setBackground(SWTResourceManager.getColor(SWT.COLOR_WHITE));
        composite_1.setLayout(new RowLayout(SWT.VERTICAL));// 先将面板设置成行式布局RowLayout,再设置为VERTICAL垂直增长
        composite_1.setSize(composite_1.computeSize(SWT.DEFAULT, SWT.DEFAULT));
        scrolledComposite.setContent(composite_1);
        // 这一行代码至关重要,关于自动增长序列,每次判断下是不是超出设定好的大小,超出了自动设置滑动条
        scrolledComposite.setMinSize(composite_1.computeSize(SWT.DEFAULT, SWT.DEFAULT));
//这里值得注意的是,如果是动态增长Composite里的组件,想要自动增长滚动条必须每次刷新后判断
        //-----------------<!--自动增长组件的代码--!>------------------------------------------------------------------
        composite_1.layout();// 重置布局,刷新界面
        // 超级注意!!!必须重置页面再判断是否增长滚动条,否则会出现无法及时增长滚动条的异常
        // 这一行代码至关重要,关于自动增长序列,每次判断下是不是超出设定好的大小,超出了设置滑动条
        scrolledComposite_chat.setMinSize(composite_1.computeSize(SWT.DEFAULT, SWT.DEFAULT));

2.1.5其他界面
其他界面基本上是自定义的Composite,每次用的时候new一个,并通过构造函数传递参数进去

eclipse里面组件太多了,网上的资料也很分散不系统,可以看看这本书——>《Eclipse SWT/JFACE 核心应用》 清华大学出版社

2.2Socket编程与多线程

建立实时的通讯软件有很多常用的模式,例如同步阻塞IO(JAVA BIO)、同步非阻塞IO(Java NIO) 、异步阻塞IO(Java NIO)、(Java AIO(NIO.2))异步非阻塞IO,由于临近期末考试,笔者也是边学边用,所以用的是最简单也是在业务层次最不成熟的方案同步阻塞IO(JAVA BIO),关于这些模式B站上面有个很好的课程,黑马程序员-Java的IO模式讲解(AIO&BIO&NIO),期考完后我会去好好看的哈哈哈,我这里写的也很简单,通俗易懂,当然问题也还有很多

2.2.1Socket编程
我这里采用的是TCP协议,总体的思路是每个客户端只需要跟服务器进行聊天,不对用户产生点对点的聊天,由服务器充当中介的角色进行消息的转发。每个客户端在连接服务器的时候会先发送一条消息,通过自己写的简单协议将自己的fromQQ,对方的toQQ和消息message以及信号值传递过去,其中信号值flag用来判断用户是想进行单聊群聊还是和机器人聊天,代码里面写的都很详细,大家可以看看,有问题多多指出一起学习哈

package com.fx.net;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import com.yc.util.YcUtil;
import com.yc.util.fxNetUtil;
/**
 * 采用TCP协议的服务器,设计思路为当有用户连接上服务器时,
 * 用一个map将其socket存起来,key为用户qq号,为群聊时,将群聊里所有用户的socket存在list里面
 * 
 * @author fx
 * @time 2021年6月30日08:44:25
 *
 */
public class ServiceDemo {
    /**
     * 分别用map和qunliao存放不同连接的scoket对象
     */
    static Map<String, Socket> map = new HashMap<String, Socket>();// 用来存放单人聊天的fromQQ和其socket对象
    // 用来放不同对象的map,包含一组socket对象
    static volatile List<Socket> list = new ArrayList<Socket>();//用volatile修饰可以使其他线程实时获取到这个变量
    public static void main(String[] args) {
        // 服务器
        // 创建一个服务器
        System.out.println("++++++++++++++++++++");
        System.out.println("腾迅QQ服务器启动");
        System.out.println("++++++++++++++++++++");
        System.out.println("服务器启动,时间:" + new SimpleDateFormat("yyyy年MM月dd日HH:mm").format(new Date()));
        // 各种对象用以连接
        Scanner inScanner = null;
        ServerSocket ss = null;
        Socket socket = null;
        String message;// 记录客户端发送过来的协议消息
        try {
            ss = new ServerSocket(9999);//占有并开放端口
            while (true) {
                // 创建一个接收连接客户端的对象 9999为开放的端口
                socket = ss.accept();
                String ip_port = new String(socket.getRemoteSocketAddress().toString()).split("/")[1];
                System.out.println("有客户端联接上来了,它是:" + ip_port);
                // 首先要接受客户端发到的第一句话,包含fromQQ,toQQ,message和flag信号值
                inScanner = new Scanner(socket.getInputStream());// 接受收入流里的字节流
                message = inScanner.nextLine();//这样接收消息高效且简单
                if ("0".equals(YcUtil.objToString(fxNetUtil.getflag(message)))) {
                    // 判断为与机器人聊天
                    new Thread(new ServiceThread(socket)).start();// new一个机器人的线程与用户交流
                } else if ("1".equals(YcUtil.objToString(fxNetUtil.getflag(message)))) {
                    // 判断为单聊,先将socket对象存到map中
                    map.put(fxNetUtil.getfromQQ(message), socket);
                    // 查找其聊天对象是否在线上,在线上应存在socket对象
                    if (map.containsValue(map.get(fxNetUtil.gettoQQ(message)))) {
                        // 存在,即通过两个好友的socket对象进行聊天
                        new Thread(new ServiceThreadsingleChat(map.get(fxNetUtil.gettoQQ(message)),
                                map.get(fxNetUtil.getfromQQ(message)))).start();
                    }
                    if (map.size() == 2) {
                        // 当有两个用户建立连接后,清空map,为后面用户腾出空间
                        map.clear();
                    }
                } else if ("2".equals(YcUtil.objToString(fxNetUtil.getflag(message)))) {
                    // 判断为群聊,先将此时访问的用户qq和其socket对象存储起来
                    list.add(socket);
                    // 每个用户进入群聊都开启一个线程进行监控,但list对象共享
                    new Thread(new ServiceThreadWechat(message, socket)).start();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {// 关闭所有资源
            if (null != socket) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != ss) {
                try {
                    ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

其他单聊群聊与和机器人聊天的代码在源码里都有,可以去参看一下,问题还是有很多滴,这里就不献丑展示了,只能说代码能实现,但一点都不优美

2.2.2接入图灵机器人API,进行机器人聊天
代码是网上东拼西凑的,我就截取来一下爬取到数据里面的有效字段,其他的也搞不懂

package com.fx.http;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
/**
 * @Author fx
 * @Date 2021年7月1日09:20:25
 */
public class HttpTest {
    static String url = null;
    static String param = "";
    public static String GetUrl(String question) {
        if (null == question) {
            question = "衡阳天气怎么样";
        }
        url = "http://api.jisuapi/iqa/query?appkey=62958a3a6ef3c56d&question=" + encode(question);
        String result = "";// 访问返回结果
        BufferedReader read = null;// 读取访问结果
        try {
            // 创建url
            URL realurl = new URL(url + "?" + param);
            // 打开连接
            URLConnection connection = realurl.openConnection();
            // 设置通用的请求属性
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 建立连接
            connection.connect();
            // 获取所有响应头字段
            Map<String, List<String>> map = connection.getHeaderFields();
            read = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
            String line;// 循环读取
            while ((line = read.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (read != null) {// 关闭流
                try {
                    read.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        // 截取需要的有效字符
        return new String(result).split("\"content\":\"")[1].split("\",\"relquestion\"")[0];
    }
    /**
     * Unicode编码
     * 
     * @param url
     * @return
     */
    public static String encode(String url)
    {
        try {
            String encodeURL = URLEncoder.encode(url, "UTF-8");
            return encodeURL;
        } catch (UnsupportedEncodingException e) {
            return "Issue while encoding" + e.getMessage();
        }
    }
    /**
     * Unicode解码
     * 
     * @param url
     * @return
     */
    public static String decode(String url) {
        try {
            String prevURL = "";
            String decodeURL = url;
            while (!prevURL.equals(decodeURL)) {
                prevURL = decodeURL;
                decodeURL = URLDecoder.decode(decodeURL, "UTF-8");
            }
            return decodeURL;
        } catch (UnsupportedEncodingException e) {
            return "Issue while decoding" + e.getMessage();
        }
    }
}

其他的问题暑假再一一完善叭,仓库里面的代码有很多试错时候留下的初期版本,没时间去掉了!!!写了半个月,现在要好好准备期末考试了

笔记我是先放在了我的笔记本里面,里面效果会好一点
笔记本地址:我的笔记,大佬轻喷!
源码地址:我的Gitee,@奉先大大鸭

本文标签: 类似于实时通讯软件SWTJava