记得几年前,在校期间写过一个聊天程序,也发布了一篇博客在 csdn 上。然而,近期有好多网友加我 索要源码,可惜的是源码早已消失在网络中了。所以,借此闲暇时间重写一次 Java 多人聊天客户端程序,以供爱好者学习交流之用。如下是每日程序的进展日志。
项目源码:
客户端 -> github.com/genialx/ChatX
服务端 -> github.com/genialx/ChatXServer
对于 Java,笔者算是新手,没有用 Java 做过实际的项目。所以,在做这个项目的过程中,进行了大量的调研工作,有很多问题都无法短时间内解决。固然,项目中的代码是很糟糕的。不过,有时间会进行深入的学习来优化项目甚至重构代码。
项目不是起于今日,目前已经完成了客户端的登陆界面,正在着手完善朋友列表的界面。
登陆界面
朋友列表
起初进行了搜索引擎,大致分为两种。一种是利用重写父类的重画方法,个人尝试了网上的代码几次,不成功,也觉得 Java 应该能提供半透明的 API,不至于还要重写,于是就放弃了。
第二种是利用 com.sun.awt.AWTUtilities 类进行设置。
但是,了解到 AWT 在 Jre8 中不再存在,同时网上描述说兼容不好,所以放弃了。
于是,在网上找到了这个很简单的 API,也刚好能够满足我的预期效果,如图“朋友列表”中的透明效果。
如果是 JScrollPane 容器的话,需要如下设置。
这是因为 JScrollPane 容器管理着视口、可选的垂直和水平滚动条以及可选的行和列标题视口。
JScrollPane 组成
首先,我在 Eclipse 的自动方法提示窗口里面找了所有 set 开头的方法,并在搜索引擎搜了几圈,最终也没能找到解决方案。感觉搜索引擎中基本上没有这个问题的提问,估计是我的关键词有问题吧。最后,通过改变 JTextField 的位置,以及 JTextField 所在容器的背景颜色来实现,输入框的内左边距的效果。
如“朋友列表”界面,由于 JScrollPane 采用了半透明的机制,导致窗口滚动时半透明的效果失效,显示灰白色的底色。如下图:
JScrollPane 滚动时半透明失效
而当触发重画方法时,JScrollPane 界面又恢复半透明效果。所以,通过给 JScrollPane 加监听鼠标滚轮事件。
但是仍然无效,原因应该是 JScrollPane 滚动时先触发 mouseWheelMoved 事件,再触发界面滚动的效果事件。所以,由于监听 mouseWheel 事件过早导致重画效果被后面的滚动效果覆盖失效。不知道这个问题该如何解决…
如上述代码,是无法显示滚动条的,尽管 scrollPanePanel 的高度(20 * 50)是大于 listScrollPane 的高度(500),如下图:
但是若将上述代码的 11 行的 setSize 方法改成 setPreferredSize 方法,滚动条就显示了。这是为啥?如下解释是来自网络的文章。
上面大概意思说的就是:
1. setPreferredSize 需要在使用布局管理器的时候使用,布局管理器会获取空间的 preferred size,因而可以生效。例如 border layout 在 north 中放入一个 panel,panel 的高度可以通过这样实现:panel.setPreferredSize(new Dimension(0, 100)); 这样就设置了一个高度为 100 的 panel,宽度随窗口变化。
2. setSize,setLocation,setBounds 方法需要在不使用布局管理器的时候使用,也就是 setLayout(null) 的时候可以使用这三个方法控制布局。
区分好这两个不同点之后,我相信你的布局会更随心所欲。
英文来源:http://stackoverflow.com/questions//java-difference-between-the-setpreferredsize-and-setsize-methods-in-compone
如“朋友列表”图中的列表(由 JPanel 构成),每一个人都是一个 JPanel 构成的。这个 JPanel 监听 mouse 事件,每当触发相应事件就会进行底色的改变。点击底色为深黄,悬浮底色变浅黄,如图:
事件代码如下:
可是当鼠标过快滑动,会频繁的触发事件,进而频繁的调用 List.this.updateG() 方法进行界面的重画。这时根据电脑的性能,会出现不用程度的卡顿显示。那么,由于对于重画机制不是很熟悉,目前先走个捷径。通过限制重画的频率来避免这个问题。因为鼠标过快滑动是问题所在,那么久避免这个条件的产生即可。如代码:
只有当两次触发间隔大于 50ms 时,才会触发重画(代码中的小于5ms的判断是排除鼠标同时触发两个事件的情况,比如鼠标移开某个 JPanel 进入另一个 JPanel 会依次触发 existed 和 entered,这两个事件的触发时间间隔小于 5ms)。
记得当时学习的时候接触到的布局常用的就是什么,FlowLayout、CardLayout、GridLayout 和 BorderLayout 等。不过,并不清楚其中的细节,忘掉了很多。也是因为懒惰,大部分布局都采用了绝对布局。优点就是编码简单易阅读,缺点就是无法做出可响应的模式。后续有时间再了解下布局这一块。
最近工作上有紧急项目,导致年后到现在都没能腾开空搞这个小项目。马上五一了,紧急项目也准备提测了,腾出时间来做做吧。
这几天完成了客户端与服务端通信的模块,随之建立了服务端项目:ChatXServer(项目地址如上文)。
这一块其实是这个项目的核心功能。简单聊下采用的实现方式和原理。
原理:阻塞 IO + 多线程
实现方式:
服务端监听本机某端口,每当接受一个新的 socket 后,将 socket 存 入自定义的客户端线程池后,启动当前客户端线程的 start 方法。进而反复以上动作(红字)。
同时服务端也开启了一个定时任务进程(crontab),以轮询当前客户端线程池中每个客户端线程,检查是否还存在“心跳”,及时处理垃圾。
上文“心跳”的含义是代表当前客户端线程是否是处于连接活跃状态,若是代表有“心跳”,若无则代表无“心跳”。
客户端监听键盘和鼠标等相关事件,将用户输入的信息发送给服务端。服务端解析来自客户端的信息,进行消息处理、业务处理或消息转发等动作。
同时客户端也在监听来自服务端发来的“命令”,以进行相应的动作。
上文中客户端与服务端通信的数据是通过序列化后进行传输的。如代码(客户端向服务端写数据):
上述代码中的 MessageManager 和 TextMessage 类需要继承 Serializable 接口以实现序列化。
写到这里,我自己都笑了。一个小项目拖到现在。而且,不知道还要拖多久。
未完待续…
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/java-jiao-cheng/5313.html