专栏亮点

  • 以 DDD 领域驱动设计的分层模型结合 Netty,编写清晰简洁可扩展的框架结构,完成仿微信聊天核心功能项目开发。
  • 使用 JavaFX 做 UI 窗体并且与业务代码分离的方式实现桌面版程序功能。在事件驱动与接口调用的方式中,使代码更加清晰、更加易于扩展。
  • 清晰的设计,明确到位的落地,让理论与实践结合起来。快速上手掌握 Netty、JavaFx、架构、库表的设计与编码。

Netty 是互联网各种框架中以及物联网里都不可缺少的一部分技术栈技能,同时它也是非常重要的一个 Socket 框架。Netty 的应用非常广泛,无论在互联网、大数据以及通信和游戏行业中,都有 Netty 的身影。比如一线大厂阿里的 RPC 框架,Dubbo 协议默认使用 Netty 作为基础通信组件,用于各节点间的内部通信。

淘宝的消息中间件 RocketMQ 的消息生产者与消费者,也是采用 Netty 作为高性能、异步通信组件。除了阿里系、淘宝系,其他很多一线大厂也都在使用 Netty 构建高性能、分布式的网络服务。

前段时间写完 Netty 系列教程,看似系统,但是回顾起来还是零散,看完后一些读者反馈无法真实的上手业务,让我能否写一写关于 Netty 实际业务中的应用。那么为了更好的让自己和大家都能拿一个熟知又有趣的场景,最好的就是模仿 PC 端的微信聊天。之后这样不仅学习到了 Netty,还让 Java 程序员使用自己的语言技术栈开发了一款桌面聊天程序。

以上专栏内容请查看本专栏第一章节:专栏学习简述以及全套源码获取

说回来往往一个新知识点的学习到上手分为三个阶段;运行 HelloWord、熟练使用 API、和最终的落地应用。而这最后一步也是最重要的一步,只要将各个知识点与实际要实现的业务功能相结合,才能不断的提升自己的技能。

对一个技能最好的掌握方式就是使用自己熟悉的工具进行实践落地,通过一点点功能的实现和阅读的一堆堆的逻辑中,建设自己对代码的认知提升,对整个框架的深化理解。

为此我开始使用 Netty+JavaFx 以及 SpringBoot 等技术栈,开始搭建仿桌面版微信聊天程序,在这个过程当中显示梳理分析功能结构。这个过程可以使用 xmind 作为你的工具,整理自己的思路。接下来开始对我需要使用的技术栈做案例测试,验证核心功能是否可以满足我的需求。验证完成后开始做架构设计以及业务流程,直到最终的编码实现功能。

专栏介绍

本专栏是作者小傅哥使用 JavaFxNetty4.xSpringBootMysql 等技术栈和偏向于 DDD 领域驱动设计方式,搭建的仿桌面版微信聊天工程实现通信核心功能。

本专栏会以三个大章节内容,逐步进行讲解:

第一部分 - UI 开发:使用 JavaFxMaven 搭建 UI 桌面工程,逐步讲解登录框体、聊天框体、对话框、好友栏等各项 UI 展示及操作事件。从而在这一章节中让 Java 程序员学会开发桌面版应用;

第二部分 - 架构设计:在这一章节中我们会使用 DDD 领域驱动设计的四层模型结构与 Netty 结合使用,架构出合理的分层框架。同时还有相应库表功能的设计。相信这些内容学习后,你一定也可以假设出更好的框架;

第三部分 - 功能实现:这部分我们主要将通信中的各项功能逐步实现,包括;登录、添加好友、对话通知、消息发送、断线重连等各项功能。最终完成整个项目的开发,同时也可以让你从实践中学会技能。

界面展示

功能演示图

登陆页面

聊天页面

添加好友

消息提醒

多图解析

可以获得什么?

  • 一整套可扩展的工程源代码,包括, JavaFx 开发的 UI 工程、Netty 客户端工程、DDD 结构模型的服务端工程以及专栏设计到的案例工程;
  • 学会系统的架构设计能力,了解 DDD 领域驱动设计的优点,可以使用 DDD 的四层架构来在平时的业务开发中进行实际落地;
  • Java 程序员掌握开发桌面版程序的能力,学会使用 JavaFx 开发出漂亮优秀的界面。学习这些内容;可以面试通关、满足工作需要、适合承接私活。

作者介绍

小傅哥,一线互联网后端工程师,CSDN 博客专家,精通 Java、Netty、Spring、SpringBoot 等技术栈,以及擅长中间件开发。

适宜人群

  • Netty 爱好者;
  • 处在刚接触 Netty 并希望以后从事此类技术工作的程序员;
  • 需要使用 Netty 开发桌面版网络通信;
  • 提升架构设计能力,写出干净整洁有章可循的代码。

购买须知

  • 本专栏为图文内容,共计 25 篇。每周更新 3 篇,预计 3 月底更新完毕;
  • 付费用户可享受文章永久阅读权限;
  • 本专栏为虚拟产品,一经付费概不退款,敬请谅解;
  • 本专栏可在 GitChat 服务号、App 及网页端 gitbook.cn 上购买,一端购买,多端阅读。

订阅福利

  • 订购本专栏可获得专属海报(在 GitChat 服务号领取),分享专属海报每成功邀请一位好友购买,即可获得 25% 的返现奖励,多邀多得,上不封顶,立即提现。

  • 提现流程:在 GitChat 服务号中点击「我 - 我的邀请 - 提现」。

  • 购买本专栏后,服务号会自动弹出入群二维码和暗号。如果你没有收到那就先关注微信服务号「GitChat」,或者加我们的小助手「GitChatty6」咨询。(入群方式可查看 第 1 篇 文末说明)。

课程内容

登陆框体实现:结构定义、输入框和登陆

一、前言

从本章节开始我们会陆续实现各个框体的 UI 开发,内容会包括;框体拆解、工程结构、代码开发,以及最后编写事件和接口。

在 JavaFx 中,一个框体包含;窗口 (Stage)、场景(Scene)、布局(Pane)、控件(Button 等) 这四方面内容。而开发过程中可以使用 xml 和编码两种方式进行处理,一般一些预定好的会使用 xml 结构,如果是随着我们业务行为触达而产生的会开发到代码中来生成。

那么接下来我们的目标是开发一个登陆框体,样式如下;

二、登陆窗体分析

按照我们的 UI 开发诉求,将整个页面进行拆解,以方便清楚知道我们的各种类型元素放置位置;

序号 模块 宽 * 高 描述
1 整体框体 540 * 415 一个整体的 4px 的圆角面板, 去掉默认的标题和工具栏
2 背景图片 540 * 158 设置的一个背景图
3 最小化、退出 43 * 32 两个同样大小的 Button
4 用户 ID 输入框 250 * 45 明文输入框
5 用户密码输入框 250 * 45 密文输入框
6 登录按钮 250 * 45 登陆按钮 Button,鼠标进入时变换背景色,点击触发登陆
7 版本展示 400 * 25 透明的无背景可以调整,一般展示版本编号如;v1.0
8 头像 100 * 100 圆角头像图片,整个可以使用 Image 等元素开发
9 标头 200 * 15 展示名称,例如;憨憨·语约
  • 以上就是我们整体窗体的一个拆解后的示意图,接下来开始按照整个示意图进行开发。
  • 如果是个人开发的新项目,一般 UI 的设计可以先在草稿设计,最后在使用工具进行具体设计。如果是公司级别会有专门的设计来出所有的图稿。
  • 关于设计中使用的元素可以从工程源码中获取,可以自己从矢量图仓库中寻找自己喜欢的;https://www.iconfont.cn

三、工程结构

itstack-naive-chat-ui-02└── src    ├── main    │   ├── java    │   │   └── org.itstack.navice.chat.ui    │   │       ├── view    │   │       │  └── Login.java    │   │       └── Application.java    │   └── resources    │       └── fxml.login    │           ├── css    │           │   └── login.css    │           ├── img    │           │   ├── close_0.png    │           │   ├── close_1.png    │           │   ├── head_default_100.png    │           │   ├── logo.png    │           │   ├── min_0.png    │           │   ├── min_1.png    │           │   └── show.png    │           └── login.fxml    └── test        └── java            └── org.itstack.test                └── ApiTest.java
  • 工程结构上我们先从简单规划;启动层、展示层、资源配置层,三方面。后面在随着开发内容的增多会不断的优化结构
  • fxml 是 JavaFx 开发的资源文件,可以设置界面展示,同时 xml 配置里可以引入 css 文件、设置元素大小等

四、代码讲解

1. login.fxml 配置

在 maven 管理下我们将配置文件放到资源文件夹下;fxml/login/login.fxml

整体外框 xml

<?import javafx.geometry.Insets?><?import javafx.scene.control.*?><?import javafx.scene.layout.Pane?><?import javafx.scene.text.Font?><Pane id="login" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"      prefWidth="540" prefHeight="415" stylesheets="@css/login.css" xmlns="http://javafx.com/javafx/8.0.121"      xmlns:fx="http://javafx.com/fxml/1">    <children>    </children></Pane>
  • 这是一个整体的面板,初步设定了面板的宽高、引入的 css 样式等
  • 接下来在 children 孩子元素集合里添加各种窗体元素

操作栏;最小化、关闭(Pane)

<Pane id="operation" prefWidth="540" prefHeight="158">    <children>        <Label id="login_logo" styleClass="logo" layoutX="10" layoutY="5" prefWidth="200" prefHeight="15" text="憨憨 · 语约" style="-fx-text-fill:#666666;"/>        <Button id="login_min" styleClass="min" layoutX="454" prefWidth="43" prefHeight="32"/>        <Button id="login_close" styleClass="close" layoutX="497" prefWidth="43" prefHeight="32"/>    </children></Pane>
  • 定义一个 Pane,并设置宽高,之后在整个面板里中添加 logo、最小化、关闭操作
  • 后面你会看到更多的 <children>,来装载元素
  • 在孩子元素中有三个元素;
  • login_logo 定义 logo 和文案,这里默认显示文字“憨憨 · 语约”
  • login_min 最小化按钮处理
  • login_close 关闭操作处理

头像(Pane)

<Pane id="headImg" layoutX="80" layoutY="200" prefWidth="100" prefHeight="100"/>
  • 这个是定义一个头像区域,也可以使用其他元素进行定义

用户 ID 输入框(TextField)

<TextField id="userId" layoutX="200" layoutY="200" prefWidth="250" prefHeight="45" promptText="账号">    <padding>        <Insets left="10"/>    </padding></TextField>
  • TextField 是一个单行明文内容输入区域,在这里不仅设置了宽高,还设置了相对位置;layoutX、layoutY
  • padding.Insets,如果你写过 CSS,可能会知道。这是一个设置输入框内,文字输入区域距离左面的空出位置。一般空出的位置可以设置一个背景图片
  • 同时我们设置了提示文字,这个在这里比较简单直接使用;promptText 即可。例如:promptText="账号"

密码输入框(PasswordField)

<PasswordField id="userPassword" layoutX="200" layoutY="255" prefWidth="250" prefHeight="45" promptText="密码">    <padding>        <Insets left="10"/>    </padding></PasswordField>
  • PasswordField 是一个单行密码内容输入区域,同样设置了宽高,以及提示文字
  • 基本的使用方式与 TextField 一致,当然在你后面后去他里面内容的时候,是明文的

登陆按钮(Button)

<Button id="login_button" styleClass="login_button" layoutX="200" layoutY="345" prefWidth="250" prefHeight="45" text="登 陆"/>
  • Button 按钮类的操作都可以使用,在这里设置好宽高,以及文字内容 text="登 陆"
  • 同样在这里我们也设置了相对的展示位置,这个位置是相对的,相遇对当前父元素

版本(Label)

<Label id="slogan" layoutX="5" layoutY="398" prefWidth="400" prefHeight="15"       text="v1.0 小傅哥 | https://bugstack.cn">    <font>        <Font size="12"/>    </font></Label>
  • Label 一般可以设置文字、图片等展示内容。是一个轻量级元素
  • 在这里我们设置了版本编号,和自己一些内容信息
  • 同时我们还设置了字体的大小,关于字体后面我们还会使用到 css(这里的 css 是 javafx 的 css,除名称不同外基本一致)

2. login.css 设计

#login{    -fx-background-radius: 4px;    -fx-border-width: 1px;    -fx-border-radius: 4px;    -fx-border-color: rgb(180,180,180);    -fx-background-color: white;}#operation{-fx-border-color: rgb(180,180,180);   -fx-border-width: 1px 1px 0 1px;   -fx-border-radius: 4px 4px 0 0;   -fx-background-image: url("/fxml/login/img/system/show.png");}.close,.close:pressed{    -fx-background-radius: 2px;    -fx-background-position: center center;    -fx-background-repeat: no-repeat;    -fx-background-size: 43px 34px;    -fx-background-color: transparent;    -fx-background-image: url("/fxml/login/img/system/close_0.png");    -fx-cursor: hand;    -fx-border-width: 0;}.close:hover{    -fx-background-color: #f45454;    -fx-background-image: url("/fxml/login/img/system/close_1.png");    -fx-border-width: 1px 1px 0 0;    -fx-border-color: rgb(180,180,180);    -fx-border-radius: 2px;}...
  • 从上面可以看到基本和我们认识的 css 代码一致,在这里我们就不全部展示这部分内容了
  • 一些基本的 javafx 中的 css 常用语法我们在第一篇中已经介绍,可以参考。很常用的包括;背景色、线条色、宽高、图片、圆角、悬停手势等。

3. 代码模块

Login.java & 登陆页面初始化

public class Login extends Stage {    private static final String RESOURCE_NAME = "/fxml/login/login.fxml";    private Parent root;    public Login() {        try {root = FXMLLoader.load(getClass().getResource(RESOURCE_NAME));        } catch (IOException e) {e.printStackTrace();        }        Scene scene = new Scene(root);        scene.setFill(Color.TRANSPARENT);        setScene(scene);        initStyle(StageStyle.TRANSPARENT);        setResizable(false);        this.getIcons().add(new Image("/fxml/login/img/system/logo.png"));    }}
  • 单个窗体的需要继承 Stage,也就是继承了窗口类,并需要在里面创建场景,才可以运行展示

  • 在这里我们加载配置元素 login.fxml,初始化窗体的基本信息

  • 在布局中我们设置了填充为透明色,以及初始化样式 StageStyle.TRANSPARENT

  • 最后我们设置了状态栏的图标样式,这里我们设置了模仿微信的样式,颜色略有差异

    this.getIcons().add(new Image("/fxml/login/img/system/logo.png"));

Application.java & 启动类

public class Application extends javafx.application.Application{    @Override    public void start(Stage primaryStage) throws Exception {Login login = new Login();        login.show();}    public static void main(String[] args) {launch(args);    }}
  • 这里的 Application 继承了 JavaFx 的 Application,并实现 start 启动

  • 在这里我们初始化登陆窗体,并通过 login.show() 调用窗体的展现

  • 上面这个结构是一个固定的模板代码,也是配置到 maven 中的启动类路径;

    <plugin>  <groupId>com.zenjava</groupId>  <artifactId>javafx-maven-plugin</artifactId>  <version>8.8.3</version>  <configuration>      <mainClass>org.itstack.navice.chat.ui.Application</mainClass>  </configuration></plugin>

五、效果演示

  1. 在类org.itstack.navice.chat.ui.Application,右键运行

  2. 不出意外效果如下;

六、总结

  • 在这一篇中我们显示拆解分析了整个窗体需要实现的功能,之后逐步使用 JavaFx 去把 UI 搭建处理。在这里使用到了代码、XML、CSS,三个技术的配合工作。
  • 通过上面的工程和代码可以初步的了解到一个简单的窗体的搭建和使用,并且设计了我们的工程结构,一个好的开始就从现在开始了
  • 同时如果你细心可能会发现我们的工程其实是在打一个 jar 包,将来共客户端使用,最后也就达到了 UI 与业务分离,这部分会随着后续章节逐步展现
登陆框体事件与接口

一、前言

  • 在上一章节中我们把登陆窗体开发完成了,并进行了效果演示。那么接下来我们就需要在这个窗体里面添加行为事件和接口,待完成内容如下;

    序号 类型 描述
    1 事件 鼠标拖拽窗体移动
    2 事件 最小化到快捷栏
    3 事件 退出当前窗体
    4 事件 使用用户 ID 和密码登陆
    5 接口 登陆成功,执行跳转操作
    6 接口 登陆失败,执行提示操作
  • 在桌面版程序开发中不同于 web。桌面版开发需要有界面的事件的发起,例如 Button 按钮点击,当接收外部条件变化后要有接口承载,例如登陆成功后的页面跳转。但是在 web 中大部分时候只需要一个 http 请求同步响应即可。

  • 另外也可能有一部分桌面开发程序中是类似同步请求和反馈的,那么在一个事件的发起后,就直接影响事件内容的变化,来改变窗体或者填充数据行为。

  • 以下的章节我们会先去非常直接简单的添加事件和接口,以更清晰的直观的了解这部分内容的开发。之后我们会进行一次小的 重构,以此来适应更好的拓展。

二、工程结构 (重构前)

itstack-naive-chat-ui-03└── src    ├── main    │   ├── java    │   │   └── org.itstack.navice.chat.ui    │   │       ├── view    │   │       │  └── Login.java    │   │       └── Application.java    │   └── resources    │       └── fxml.login    │           ├── css    │           ├── img    │           └── login.fxml    └── test        └── java            └── org.itstack.test                └── ApiTest.java
  • 在目前的工程结构下,直接在里面开发事件和接口。

三、事件内容讲解 (重构前)

接下来我们会在现有代码中,org.itstack.navice.chat.ui.view.Login.java,进行编写事件和接口。

1. 鼠标移动窗体事件

 private double xOffset; private double yOffset;private void move() {    root.setOnMousePressed(event -> {xOffset = getX() - event.getScreenX();        yOffset = getY()- event.getScreenY();        root.setCursor(Cursor.CLOSED_HAND);    });    root.setOnMouseDragged(event -> {setX(event.getScreenX() + xOffset);        setY(event.getScreenY() + yOffset);});    root.setOnMouseReleased(event -> {root.setCursor(Cursor.DEFAULT);    });}
  • 这里的 root 是我们整个登陆窗体的元素面板,可以通过它来设置一些行为设置或者还可以通过 ID 查找根里面还有的元素 root.lookup
  • 在我们所有使用的桌面程序里,都是可以通过鼠标拖拽来移动位置的。那么这里我们是定义三个鼠标事件,来实现窗体的移动,如下;
  • setOnMousePressed;鼠标按下事件,这个时候记录窗体位置
  • setOnMouseDragged;鼠标拖动事件,这个设置 setX、setY
  • setOnMouseReleased;鼠标释放事件,这个时候恢复默认鼠标样式,最终完成了整个窗口的拖拽过程

2. 最小化窗体事件

private void min() {Button login_min = $("login_min", Button.class);    login_min.setOnAction(event -> {System.out.println("最小化窗体");        setIconified(true);    });}
  • 这个事件比较简单,只需要设置按钮点击事件后执行;setIconified(true) 即可,如果有些快捷键操作,弹出窗体可以动态设置为 false

3. 退出窗体事件

private void quit() {Button login_close = $("login_close", Button.class);    login_close.setOnAction(event -> {System.out.println("退出窗体");        close();        System.exit(0);    });}
  • 最小化与退出是一对的事件,退出事件同样需要绑定按钮,并执行 close() 操作以退出窗体,并执行程序退出 System.exit(0)
  • 另外在 socket 通信的时候,退出还需要断开服务端连接,记录个人状态等操作。例如;某某地方登陆、某某地方退出、时间、设备等信息,后续在通信开发中我们会陆续完善部分功能

4. 登陆操作事件

private void login() {TextField userId = $("userId", TextField.class);    PasswordField userPassword = $("userPassword", PasswordField.class);    $("login_button", Button.class).setOnAction(event -> {System.out.println("登陆操作");        System.out.println("用户 ID:" + userId.getText());        System.out.println("用户密码:" + userPassword.getText());    });}
  • 登陆操作是我们在点击按钮时,获取输入框内的‘用户 ID’与‘用户密码’,发送给服务端进行验证。当然这里的真实业务场景下,不会直接明文传输,会进行非对称加密,并在服务端解析后做登陆验证。
  • 登陆验证完成后,开始收到异步消息。如果验证成功则可以进行页面跳转,否则进行提示。
  • 那么现在你可能会想到这部分怎么和通信交互,以及事件的触发,这部分内容我们会逐步讲解。

四、工程结构 (重构后)

itstack-naive-chat-ui-04└── src    ├── main    │   ├── java    │   │   └── org.itstack.navice.chat.ui    │   │       ├── view    │   │       │  └── login    │   │       │  │    ├── ILoginEvent.java    │   │       │  │    ├── ILoginMethod.java    │   │       │  │    ├── LoginController.java    │   │       │  │    ├── LoginEventDefine.java    │   │       │  │    ├── LoginInit.java    │   │       │  │    └── LoginView.java    │   │       │  └── UIObject.java    │   │       └── Application.java    │   └── resources    │       └── fxml.login    │           ├── css    │           ├── img    │           └── login.fxml    └── test        └── java            └── org.itstack.test                └── ApiTest.java
  • 从上面新的结构可以看到,原来我们仅是一个类被抽取出这么多类,而同时每一个都被赋予了不同的功能,以整洁我们的代码;

五、代码讲解 (重构后)

抽象后的类功能结构,如下;

序号 描述
1 ILoginEvent 事件接口类,具体实现交给调用方。例如我们在点击登陆后将属于窗体的功能处理完毕后,实际的验证交给外部
2 ILoginMethod 方法接口类,在上面我们说过桌面程序的开发基本都是事件触达和等待回调,那么我们给外部提供接口主要用于类似登陆处理完毕后,来执行相应方法进行窗体切换或者数据填充
3 LoginController 窗体的控制管理类,也是一个窗体的管家;因为它会继承窗体的装载、实现接口方法、初始化界面、初始化事件定义
4 LoginEventDefine 窗体事件定义,例如将登陆、最小化、退出等在这里完成定义
5 LoginInit 窗体的初始化操作,可以创建一些待填充的元素
6 LoginView 窗体的展示,主要用于扩展一些随着用户操作新展示的元素,例如后续在聊天窗体新增的消息提醒等
7 UIObject UI 父类定义,这是一个抽象类,提供了基础的初始化内容和接口,以及定义抽象方法

1. 窗体事件接口

ILoginEvent.java & 窗体事件接口

public interface ILoginEvent {    /**     * 登陆验证     * @param userId        用户 ID     * @param userPassword  用户密码     */    void doLoginCheck(String userId, String userPassword);}
  • 事件方法里提供了登陆所需的参数,用户 ID、用户密码,如果是实际业务开发还会需要传递;IP 地址、设备信息、请求时间等信息,用于判断是否正常登陆

2. 窗体方法接口

ILoginMethod.java & 窗体方法接口

public interface ILoginMethod {    /**     * 打开登陆窗口     */    void doShow();    /**     * 登陆失败     */    void doLoginError();    /**     * 登陆成功;跳转聊天窗口 [关闭登陆窗口,打开新窗口]     */    void doLoginSuccess();}
  • 这里面定义登陆操作的基本方法,按需可以提供一些入参。
  • doShow(); 是调用展示登陆窗体的接口,因为在我们重构定义后,调用方并不能直接使用 show() 来展示页面
  • doLoginError(); 登陆失败的操作
  • doLoginSuccess(); 登陆成功的操作,主要包含关闭现有页面,打开新的页面。这里新的页面,也就是我们后续的聊天主窗体

3. 窗体的控制管理类

LoginController.java & 窗体的控制管理类

public class LoginController extends LoginInit implements ILoginMethod {    private LoginView loginView;    private LoginEventDefine loginEventDefine;    public LoginController(ILoginEvent loginEvent) {super(loginEvent);    }    @Override    public void initView() {loginView = new LoginView(this, loginEvent);    }    @Override    public void initEventDefine() {loginEventDefine = new LoginEventDefine(this, loginEvent, this);    }    @Override    public void doShow() {super.show();    }    @Override    public void doLoginError() {System.out.println("登陆失败,执行提示操作");    }    @Override    public void doLoginSuccess() {System.out.println("登陆成功,执行跳转操作");        // 关闭原窗口        close();}}
  • 首先可以看到这里继承了窗体的初始化,实现了窗体的接口定义。这样的方式可以更加方便外部的调用,同时内部的逻辑也会更加清晰。
  • initView(),初始化了窗体页面,如果随着后续的窗体内容的增加,这部分初始化的内容也会有所增加。
  • initEventDefine(),等窗体初始化完成后,我们就可以初始化我们的事件定义。
  • 接下来我们后面看到的代码就是关于接口的具体的实现,供外部调用的。

4. 窗体事件定义

LoginEventDefine.java & 窗体事件定义

public class LoginEventDefine {    private LoginInit loginInit;    private ILoginEvent loginEvent;    private ILoginMethod loginMethod;    public LoginEventDefine(LoginInit loginInit, ILoginEvent loginEvent, ILoginMethod loginMethod) {        this.loginInit = loginInit;        this.loginEvent = loginEvent;        this.loginMethod = loginMethod;        loginInit.move();        min();        quit();        doEventLogin();}    // 事件;最小化    private void min() {        loginInit.login_min.setOnAction(event -> {loginInit.setIconified(true);        });}    // 事件;退出    private void quit() {        loginInit.login_close.setOnAction(event -> {loginInit.close();            System.exit(0);        });}    // 事件;登陆    private void doEventLogin() {        loginInit.login_button.setOnAction(event -> {loginEvent.doLoginCheck(loginInit.userId.getText(), loginInit.userPassword.getText());});    }}
  • 这里的事件定义基本和前面一样,唯一不同的是我们不需要在这里通过 id 获取元素
  • 事件定义完成后,都会交给构造方法进行初始化。当然,如果后面这部分内容较多,还可以抽象为配置

5. 窗体的初始化操作

LoginInit.java & 窗体的初始化操作

public abstract class LoginInit extends UIObject {    private static final String RESOURCE_NAME = "/fxml/login/login.fxml";    protected ILoginEvent loginEvent;    public Button login_min;          // 登陆窗口最小化    public Button login_close;        // 登陆窗口退出    public Button login_button;       // 登陆按钮    public TextField userId;          // 用户账户窗口    public PasswordField userPassword;// 用户密码窗口    LoginInit(ILoginEvent loginEvent) {        this.loginEvent = loginEvent;        try {root = FXMLLoader.load(getClass().getResource(RESOURCE_NAME));        } catch (IOException e) {e.printStackTrace();        }        Scene scene = new Scene(root);        scene.setFill(Color.TRANSPARENT);        setScene(scene);        initStyle(StageStyle.TRANSPARENT);        setResizable(false);        this.getIcons().add(new Image("/fxml/login/img/system/logo.png"));        obtain();        initView();        initEventDefine();}    private void obtain() {login_min = $("login_min", Button.class);        login_close = $("login_close", Button.class);        login_button = $("login_button", Button.class);        userId = $("userId", TextField.class);        userPassword = $("userPassword", PasswordField.class);    }}
  • 这个类是一个抽象类,同时继承了 UI 父类方法。
  • 并且在构造函数中执行了初始化操作;initView()initEventDefine()
  • obtain() 方法中可以看到,我们这里就已经初始化获取了基本需要的元素,这样也方面我们后续的使用,不需要重复获取。

6. 窗体的展示

LoginView.java & 窗体的展示

public class LoginView {    private LoginInit loginInit;    private ILoginEvent loginEvent;    public LoginView(LoginInit loginInit, ILoginEvent loginEvent) {        this.loginInit = loginInit;        this.loginEvent = loginEvent;    }}
  • 这部分内容当前类主要承担着窗体初始化和窗体的事件的一个构造函数,对于后续其他复杂页面初始化更多的预定义元素才会更有用。

7. UI 父类定义

UIObject.java & UI 父类定义

public abstract class UIObject extends Stage {    protected Parent root;    private double xOffset;    private double yOffset;    public  <T> T $(String id, Class<T> clazz) {return (T) root.lookup("#" + id);    }    public void clearViewListSelectedAll(ListView<Pane>... listViews) {for (ListView<Pane> listView : listViews) {listView.getSelectionModel().clearSelection();}    }    public void move() {        root.setOnMousePressed(event -> {xOffset = getX() - event.getScreenX();            yOffset = getY()- event.getScreenY();            root.setCursor(Cursor.CLOSED_HAND);        });        root.setOnMouseDragged(event -> {setX(event.getScreenX() + xOffset);            setY(event.getScreenY() + yOffset);});        root.setOnMouseReleased(event -> {root.setCursor(Cursor.DEFAULT);        });}    // 初始化页面    public abstract void initView();    // 初始化事件定义    public abstract void initEventDefine();}
  • 这里我们将一些公用的方法和事件操作抽象为父类,共所有的框体使用
  • 同时我们预定了两个抽象类函数,initView()initEventDefine(),这样主要为了方便统一名称下的初始化操作。尤其在团队编码中,更加重要

六、效果演示

  • 首先我们在 org.itstack.naive.chat.ui.Application 中,添加我们的窗体启动代码,同时我们还实现了事件并传给构造函数;

    public class Application extends javafx.application.Application {  @Override  public void start(Stage primaryStage) throws Exception {ILoginMethod login = new LoginController((userId, userPassword) -> {System.out.println("登陆 userId:" + userId + "userPassword:" + userPassword);      });
      login.doShow();}

    public static void main(String[] args) {launch(args); }}

  • 点击运行,效果如下;

七、总结

  • 在这章节中我们定义了事件和实现了接口,并且我们将原有代码进行重构,以更加适合扩展的方式进行编码。
  • 关于最初的代码和重构的结构,需要好好理解下,也许你心里还有更佳的方案,那么也是可以尝试的,找到自己最合适的。
  • 等到我们开始实现具体业务流程开发的时候,就会体会到这么拆解架构的原因。因为我们的目标是让 UI 里不要写逻辑,保持 前后端分离
专栏学习简述以及全套源码获取
PC 端微信页面拆分及 JavaFx 使用
聊天框体实现:整体结构定义、侧边栏
聊天框体实现:对话框
聊天框体实现:对话聊天框
聊天框体实现:好友栏
聊天框体实现:好友填充框
聊天框体事件定义
练习篇:聊天表情框体实现
解答篇:聊天表情框体实现
服务端架构设计
通信协议包定义
客户端架构设计
数据库表结构设计
登录功能实现
搜索和添加好友
对话通知与应答
用户与好友通信
用户与群组通信
断线重连恢复通信
服务端控制台搭建
练习篇:聊天表情发送功能实现
解答篇:聊天表情发送功能实现

阅读全文: http://gitbook.cn/gitchat/column/5e5d29ac3fbd2d3f5d05e05f

Netty + JavaFx 实战:仿桌面版微信聊天相关推荐

  1. 《Netty+JavaFx实战:仿桌面版微信聊天》代码开源、上云部署、视频讲解,只为让你给点个Star!

    作者:小傅哥 博客:https://bugstack.cn 视频:https://mp.weixin.qq.com/s/OmXCY4fTfDpkvjlg5ME0ZA 源码:https://github ...

  2. 毕业设计写这个你就赢了《Netty + JavaFx 实战:仿桌面版微信聊天》

    一.前言 本项目是作者小傅哥使用JavaFx.Netty4.x.SpringBoot.Mysql等技术栈和偏向于DDD领域驱动设计方式,搭建的仿桌面版微信聊天工程实现通信核心功能.如果本项目能为您提供 ...

  3. jquery 背景特效实现_html5实现的仿网页版微信聊天界面效果源码

    码农那点事儿 关注我们,一起学习进步 这是一款基于html5实现的仿网页版微信聊天界面效果源码,可实现微信网页版聊天界面效果,在编辑框编辑文字之后按Ctrl+Enter键即可提交文字到聊天对话框上.整 ...

  4. html5仿网页版微信聊天界面代码

    2019独角兽企业重金招聘Python工程师标准>>> html5仿网页版微信聊天界面代码 转载于:https://my.oschina.net/u/1266171/blog/783 ...

  5. 老板到底能不能,监控到电脑版微信聊天?

    这是「进击的Coder」的第 423 篇技术分享 作者:车小胖 来源:车小胖谈网络 " 阅读本文大概需要 3 分钟. " 三个月前其实已经写了一篇,但是在那篇文章里只提到了网页版微 ...

  6. java实现仿QQ,微信聊天时间格式化(聊天记录中的时间戳标记)

    package com.wei.wework.util;import java.text.ParseException; import java.text.SimpleDateFormat; impo ...

  7. 网页版微信聊天机器人源码调试遇到的几个问题

    最近加了一个源码阅读的群,其中这周的源码是网页版微信机器人 github代码链接 https://github.com/Urinx/WeixinBot 在这个源码的调试过程中遇到以下问题,不过仍然还没 ...

  8. Android仿QQ、微信聊天界面长按提示框效果

    最近在工作项目中要实现一个长按提示 "复制" 的功能,类似于QQ.微信聊天界面长按提示框效果,本来想偷懒在网上找个开源的项目用,但是看了好几个都不是很满意,所以就打算按照自己的思路 ...

  9. php++仿网页版微信,vue+web端仿微信网页版聊天室功能

    一.项目介绍 基于Vue2.5.6+Vuex+vue-cli+vue-router+vue-gemini-scrollbar+swiper+elementUI等技术混合架构开发的仿微信web端聊天室- ...

  10. 高仿android 版微信(服务端,客户端都有)

    做开发已经很多年,突然心血来潮,想仿微信做个即时聊天的应用练练手. 话不多说,说说实现方式,后端采用.net web api ,轻车熟路,推送基于个推,用.net consoleApp 实现.目前实现 ...

最新文章

  1. 19.并发容器之BlockingQueue
  2. 基于嵌入式操作系统VxWorks的多任务并发程序设计――中断与任务
  3. 支付接口调用成功后如何让前端知道_开发口中的「接口」到底是什么
  4. redis映射的概念_Redis存储总是心里没底?你大概漏了这些数据结构原理
  5. python 车牌识别简单_简单30行Python代码让你玩转YOLO目标识别检测!
  6. testbench文件显示波形_如何编写testbench的总结(非常实用的总结)
  7. python实现模糊综合评价法(FCE)
  8. fftshift函数详解
  9. H3C WA4320H-ACN 无线AP硬改支持集客256M内存启动
  10. html网页简单实现图片轮播效果,html+css+js简单实现图片轮播效果
  11. Web浏览器与Web服务器之间的通信过程
  12. ML-Agents 安装配置 【ML-Agents 官方文档翻译(ML-Agent 1.9.1,Unity 2018-2020)】
  13. Windows 10安装labelImg(有压缩包链接)标注弹孔,生成.xml文件。
  14. 【循序渐进学Python】Python中的循环(二)——while循环与嵌套
  15. [小说]魔王冢(63)正牌蚩尤
  16. java计算机毕业设计vue.js开发红酒网站源码+mysql数据库+系统+lw文档+部署
  17. (翻译)A Deep Learning-Based Approach to Progressive Vehicle Re-identification for Urban Surveillance
  18. 2020.7.18【算协集训】[第1次积分赛]
  19. excel中整列填充数据方法
  20. vue组件通讯六种方法。

热门文章

  1. 固态硬盘用软件测试掉速严重,固态硬盘为什么会“掉速”?
  2. 软件工程课程设计-ch小说网站
  3. FTRL之FM和LR实战(使用稀疏数据进行实战)
  4. 拼多多和酷家乐面试总结(已拿offer)
  5. oracle复合结构,动名词的复合结构作宾语
  6. python把英语句子成分字母_英语句子成分
  7. 工具 | 2的次方对照表
  8. Win11怎么关闭开机自启动软件
  9. 程序员需要了解英国文学
  10. 08python excel一键式统计0.11版本修正利息收入费用正负表达问题