JavaFX桌面应用-MVC模式开发,“真香”
使用mvc模块开发JavaFX桌面应用在JavaFX系列文章第一篇 JavaFX桌面应用开发-HelloWorld 已经提到过,这里单独整理使用mvc模式开发开发的流程。
~ JavaFX桌面应用开发系列文章 ~
- JavaFX桌面应用开发-HelloWorld
- JavaFX布局神器-SceneBuilder
- JavaFX让UI更美观-CSS样式
- JavaFX桌面应用-为什么应用老是“未响应”
- JavaFX桌面应用-MVC模式开发,“真香” (本文)
- JavaFX桌面应用-loading界面
- JavaFX桌面应用-表格用法
对于mvc模式,用struts2或springmvc开发JavaEE项目的程序员来说并不陌生,mvc模式分为control(控制层)、 model(模型层)和view(视图层)。
以springmvc为例:
@Controller 对应控制层(struts2对应的是action)
model 对应模型层(java bean)
jsp及各种视图模板 对应视图层
那么对JavaFX桌面应用来说,对应关系如下:
fx:controller 控制层
javafx.beans.property 模型层
fxml 视图层
下面是一个简单的mvc模式的JavaFX案例:
1. 控制层
JavaFX的控制层可以是一个简单的Java类,如果需要进行初始化那么需要实现Initializable接口。
public class TableUI implements Initializable {
// 对应视图层的Label标签,fx:id="time"
public Label time;
// 模型层的model
private TableModel model = new TableModel();
@Override
public void initialize(URL location, ResourceBundle resources) {
// 将视图层的Label控件和模型层的time属性进行双向绑定,这个跟vue的双向绑定有点类似。
time.textProperty().bindBidirectional(model.timeProperty());
// 启动新线程定时改变模型中的time属性,
executeTimeWork();
}
private void executeTimeWork() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
new Thread(() -> {
while (true) {
// 注意:这里只需要改变model中的time属性即可,视图层的Label信息会跟着调整
// 因为在initialize方法中已经将time属性绑定在Label控件中了。
Platform.runLater(() -> model.setTime(sdf.format(new Date())));
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException ignore) {
}
}
}).start();
}
}
2. 模型层
模型层其实就是一个贫血模式的Java Bean,不过需要注意的是模型字段声明需要用到javafx.beans.property的相关类,因为这些类实现了观察者模式,当model的值更新时,会更新UI。
public class TableModel {
// 这里必须使用property的相关类
private StringProperty time = new SimpleStringProperty();
// Getter/Setter推荐使用IDEA来生成会生成下面3个方法,如果是用eclipse将只生成普通的getter/setter
public String getTime() {
return time.get();
}
public StringProperty timeProperty() {
return time;
}
public void setTime(String time) {
this.time.set(time);
}
}
3 视图层
视图层比较简单,使用fxml排版即可,这里仅使用一个Label来显示时间。
<BorderPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.itqn.gui.javafx.wx.table.TableUI">
<!-- 上面的fx:controller绑定了Controller,即绑定了控制层 -->
<top>
<HBox alignment="CENTER" prefHeight="40.0" spacing="20.0" BorderPane.alignment="CENTER">
<children>
<!-- fx:id跟控制层的time属性绑定 -->
<Label fx:id="time" alignment="CENTER" contentDisplay="CENTER" prefWidth="200.0" text="Label" />
</children>
<BorderPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</BorderPane.margin>
</HBox>
</top>
</BorderPane>
~ 最终效果图 ~
总的来说,使用mvc模式来开发JavaFX要比手动管理UI控件也业务数据简便很多很多,而且model和UI控件是双向绑定的。
4. 复合模型层
如果只是简单的model数据,可以为每个控件声明一个队名的属性,并将双方绑定即可,但是如果遇到一些复杂的模型层数据,可能就要用到复合模型了,比如表格等。
对于UI层中还有表格且还有其他UI控件的情况,可以使用复合模型层,因为表格这些控件需要为行单独定义模型,所以需要将多个模型进行组合。
- 单独定义行模型TableColumnModel
假设表格的每一行是一项任务,由id和标题(title)组成,可以将模型定义如下, 其中selected是每一个行前面的复选框,progress是任务完成的进度。
public class TableColumnModel {
private Work work;
private BooleanProperty selected = new SimpleBooleanProperty();
private IntegerProperty id = new SimpleIntegerProperty();
private StringProperty title = new SimpleStringProperty();
private DoubleProperty progress = new SimpleDoubleProperty();
public static TableColumnModel fromWork(Work work) {
TableColumnModel model = new TableColumnModel();
model.work = work;
model.setSelected(false);
model.setId(work.getId());
model.setTitle(work.getTitle());
model.setProgress(0);
return model;
}
// 这里省略getter/setter
}
- 使用复合模型定义整个UI的模型
这里UI有一个Label和一个Table,而Table的模型是一个List集合,Table的行模型使用TableColumnModel。
public class TableModel {
// 时间Label模型
private StringProperty time = new SimpleStringProperty();
// 表格组合TableColumnModel模型
private ObservableList<TableColumnModel> tableList = FXCollections.observableArrayList();
public String getTime() {
return time.get();
}
public StringProperty timeProperty() {
return time;
}
public void setTime(String time) {
this.time.set(time);
}
public ObservableList<TableColumnModel> getTableList() {
return tableList;
}
public void setTableList(ObservableList<TableColumnModel> tableList) {
this.tableList = tableList;
}
}
这样复合模型就定义好了。
5. 自定义表格列控件
表格列控件可以使用fxml直接定义,也可以使用java代码来构建,一般来说只是简单的显示一些数据的表格可以在fxml中直接定义控件,如果需要有复杂的控件或者组合控件,那么推荐使用java代码来构建。
像这种比较复杂的列控件,就可以使用java代码来构建了,列控件使用TableCell来构建,JavaFX提供了一些默认的实现:
CheckBoxTableCell
ChoiceBoxTableCell
ComboBoxTableCell
ProgressBarTableCell
TextFieldTableCell
除了JavaFX提供的TableCell,可以通过column.setCellFactory()构建自定义的TabelCell。
对于上面的4种控件,可以分别通过以下方式来构建:
- 复选框
表格中的复选框直接使用CheckBoxTableCell来构建即可。
public static TableColumn checkboxColumn(String text, String field, int width) {
TableColumn column = new TableColumn();
column.setText(text);
column.setPrefWidth(width);
column.setCellValueFactory(new PropertyValueFactory(field));
column.setCellFactory(CheckBoxTableCell.forTableColumn(column));
return column;
}
- 普通文本
表格中的普通文本不需要额外设置CellFactory。
public static TableColumn textColumn(String text, String field, int width) {
TableColumn column = new TableColumn();
column.setText(text);
column.setPrefWidth(width);
column.setCellValueFactory(new PropertyValueFactory(field));
return column;
}
- 进度条
进度条可以需要改变一下显示效果,即在进度条后面显示进度,采用Label和ProgressBar组合而成。
public static TableColumn progressColumn(String text, String field, int width) {
TableColumn column = new TableColumn();
column.setText(text);
column.setPrefWidth(width);
column.setCellValueFactory(new PropertyValueFactory(field));
column.setCellFactory(v -> {
return new TableCell<Object, Double>() {
private HBox hBox = new HBox();
private Label progressLabel = new Label("0% ");
private ProgressBar progressBar = new ProgressBar();
{
progressLabel.setPrefWidth(50);
progressLabel.setAlignment(Pos.CENTER_RIGHT);
hBox.getChildren().addAll(progressBar, progressLabel);
}
@Override
protected void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
progressBar.setProgress(item);
progressLabel.setText((int) ((item * 100)) + "% ");
setGraphic(hBox);
}
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
};
});
return column;
}
- 操作按钮组
操作按钮组的实现跟进度条的构建方式是一样的,只是将Label和ProgressBar换成两个Button即可,这里不再贴代码。
所有列控件构建好之后,只需要将所有列加入到表格中即可。
private void buildTableColumn() {
TableColumn<TableColumnModel, Boolean> selected = TableColumnBuilder.checkboxColumn("", "selected", 40);
TableColumn<TableColumnModel, Integer> id = TableColumnBuilder.textColumn("ID", "id", 60);
TableColumn<TableColumnModel, String> title = TableColumnBuilder.textColumn("名称", "title", 180);
TableColumn<TableColumnModel, Double> progress = TableColumnBuilder.progressColumn("进度", "progress", 150);
TableColumn<TableColumnModel, Integer> operator = TableColumnBuilder.operatorColumn("操作", "id", 130, this::operatorConsumer);
table.getColumns().addAll(selected, id, title, progress, operator);
}
6. 结合业务使用
一般来说,表格的数据是有业务模块加载出来的出来的,为了模拟真正的流程,这里采用三层架构来实现业务分层,即表示层(mvc),业务层(service),数据层(dao)。
这里模拟实现的功能是:
- Controller从Service拉取数据放到Table中。
- 当用户点击加载的时候,模拟任务处理进度。
- 当用户点击删除的时候,将任务从列表中删除。
完整是Service实现如下:
public class TableService {
private TableModel model;
public TableService(TableModel model) {
this.model = model;
}
public void loadTableList() {
// 这里省略了dao层,直接随机生成模拟数据
String[] works = new String[]{"Hi IT青年", "JavaFX MVC", "https://www.cnblogs.com/itqn/", "Wx公众号:HiIT青年"};
for (int i = 0; i < 10; i++) {
model.getTableList().add(TableColumnModel.fromWork(new Work(i + 1, works[(int) (Math.random() * works.length)])));
}
}
// 处理任务加载,进度更新
public void executeLoadWork(Integer id) {
if (id == null) {
return;
}
Optional<TableColumnModel> opt = model.getTableList().stream().filter(i -> i.getId() == id).findFirst();
if (opt.isPresent()) {
TableColumnModel cm = opt.get();
new Thread(() -> {
while (cm.getProgress() < 1) {
cm.setProgress(cm.getProgress() + 0.01);
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException ignore) {
}
}
cm.setProgress(1);
}).start();
}
}
// 删除任务,将任务从模型中删除,实际可以还需要操作dao.
public void executeDeleteWork(Integer id) {
if (id == null) {
return;
}
model.getTableList().removeIf(i -> i.getId() == id);
}
}
最终的效果:
=========================================================
文章中的源码可 关注 公众号 “HiIT青年” ,发送 “javafx-mvc” 获取。
关注公众号,阅读更多文章。
JavaFX桌面应用-MVC模式开发,“真香”的更多相关文章
- 【Web开发】Mean web开发 01-Express实现MVC模式开发
简介 Mean是JavaScript的全栈开发框架.更多介绍 用Express实现MVC模式开发是Mean Web全栈开发中的一部分. Express 是一个基于 Node.js 平台的极简.灵活的 ...
- 【转】利用MVC模式开发Java应用程序[组图]
Java是一种面向对象的语言,是实现面向对象编程的强大工具.我们在实际编程中,应该运用并发挥其最大效能.但是,要利用面向对象编程思想,自己独立开 发出好的Java应用程序,非凡是大.中型程序,并不是一 ...
- Extjs MVC模式开发,循序渐进(一)
本文讲述extjs mvc的Helloworld,tabPanel,event,页面布局layout等内容. 本页包含:MVC模式案例(一)~MVC模式案例(六),从搭建extjs mvc到点击按钮生 ...
- .net使用mvc模式开发web应用 模型与视图间的数据处理
http://www.cnblogs.com/JeffreyZhao/archive/2009/02/27/mvc-use-strong-type-everywhere.html#3427764 本文 ...
- Angular——MVC模式开发实战
创建项目 创建工作目录 使用bower下载需要插件 git init.add.commit之后得到分支master,再创建developer分支,然后再此分支上进行具体功能开发 MVC架构 之前小项目 ...
- 使用MVC模式开发一简单的销售额查询系统
与上一篇比较,只改变了index.jsp文件中form的提交路径 <form action="ShowServlet" method="post"> ...
- 基于MVC模式开发的后台框架
1.ThinkCMF 2.NFine快速开发平台 3.力软快速开发框架 如有好的开发框架希望可以一起交流
- mvc模式开发
- JavaFX桌面应用开发系列文章
~ JavaFX桌面应用开发系列文章汇总篇 ~ JavaFX桌面应用开发-HelloWorld JavaFX布局神器-SceneBuilder JavaFX让UI更美观-CSS样式 JavaFX桌面应 ...
随机推荐
- 题解 CF1354D 【Multiset】
考试拿到题,一看,这不是权值线段树吗? 思路 使用线段树每个节点维护该区间内元素出现次数. 根据题目,对于加入.删除元素,我们可以单点修改(\(+1\).\(-1\)),对于输出,我们可 随便 遍历找 ...
- P.SDA1.DEV - 一个没有服务器的图床
图床特色 P.SDA1.DEV的愿景是为大家提供一个免费.长期稳定外链分享图片的选择. P.SDA1.DEV的主要特点有: 完全建构在Serverless云服务上,致力于提供(墙外)可用性99.9%的 ...
- css选择器大全
1.元素选择器 这是最基本的CSS选择器,HTML文档中的元素本身就是一个选择器: p {line-height:1.5em; margin-bottom:1em;} //设置p元素行高1.5em,距 ...
- Pull后产生多余的log(Merge branch 'master' of ...)
第一步: git reset --hard 73d0d18425ae55195068d39b3304303ac43b521a 第二步: git push -f origin feature/PAC_1 ...
- 设计模式:decorator模式
两点: 继承同一虚接口,实现数据一致性 桥接方式指向被装饰类 目的:在不改变被装饰类功能的前提下增加新功能 特点:继承是子类和父类强耦合,桥接是低耦合 例子: class Print //抽象接口 { ...
- 004.Nginx日志配置及状态监控
一 Nginx请求简介 1.1 请求头部 对于HTTP而言,客户端负责发起request请求,服务端负责response响应. request:包括请求行.请求头部.请求数据: response:包括 ...
- Google免费新书-《构建安全&可靠的系统》
前段时间riusksk在公众号分享的Google安全团队的新书,好书,全英原版,开源免费. 免费下载地址:https://static.googleusercontent.com/media/land ...
- dp入门例题(1)
按摩师问题 https://leetcode-cn.com/problems/the-masseuse-lcci/ (找好状态转移方程) 今天只和昨天的状态相关,依然是分类讨论: 今天不接受预约:或者 ...
- 8-Pandas之如何查找存在缺失值的行(any与all详解)
若有一份数据,简略如下: 国家 啤酒消耗量 烈酒消耗量 红酒消耗量 总酒精消耗量 所在大洲 0 Afghanistan 0.0 0.0 0.0 0.0 AS 1 Albania 89.0 132. ...
- PHP simplexml_load_file() 函数
实例 转换 XML 文件为 SimpleXMLElement 对象,然后输出对象的键和元素: <?php高佣联盟 www.cgewang.com$xml=simplexml_load_file( ...