大三上_javafx项目

大三上_javafx项目

  • 前言
  • 一、效果展示
    • 1、用户
    • 2、管理员
  • 二、过程
    • 1、前后端交互
      • 1)第一个页面
      • 2)切换页面
    • 2、项目架构的搭建
      • 1)项目文件目录
      • 2)记录当前用户
      • 3)自动编译
    • 3、文件问题
      • 1)文件选择与上传
      • 2)导出文件
      • 3)打开本地文件
    • 4、实时刷新及优化
    • 5、打包
      • 1)如何打包
      • 2)打包后配置文件的路径问题
      • 3)打包后上传、显示图片
        • (1)显示图片
        • (2)上传图片
    • 6、写前端时遇到的问题

前言

虽然标题写的是大三上,但实际上这个项目是从暑假的八月二十几号就开始做了。

记得那天中午吃饭的时候,Java老师突然给我发了个消息,问我有没有时间出差,他想让我单独负责一个项目

我看到这条消息当场就傻眼了,忙推辞说我没有能力单独负责一个项目。

但Java老师特别看好我,他说我的学习能力很强,做项目做的比很多研究生都好,还说如果我一个人忙不过来,他会安排一个研究生给我打下手。。。。

大三上_javafx项目_第1张图片

说真的,我这辈子都没有这么受宠若惊过

不过还好,后来可能是因为老师发现这个项目比他想象得复杂,所以他还是找了之前那个带我的研究生学长来带我,两个人做这个项目。

(这属实让我松了一口气,毕竟要是真让我带研究生,我可不好意思给研究生分配任务。。。。)

经过一个多月的努力,现在这个项目基本是完成了。

虽然从难度上来说,学长负责的数据解析部分(即从串口服务器读取数据并解析)比我负责的部分难。但从代码量来看,我完成了整个项目的百分之70~80

(这个项目为商业项目,所以本博客不会大量地贴出项目中的代码,只用来记录做项目的历程和遇到的困难)

一、效果展示

登陆界面

大三上_javafx项目_第2张图片

点击“修改登陆页面信息”按钮,弹出如下弹窗,可以修改登陆页面的公司名、协议、图片等信息

大三上_javafx项目_第3张图片

1、用户

使用普通用户账号登录显示如下界面,可实时监控各个厂区的状态

在左侧进行勾选或者点击图上的绿色小圆圈可查看具体信息

大三上_javafx项目_第4张图片

再进行勾选还可以查看更详细的内容,这里不再演示

点击左上角链接可进行搜索

选择筛选条件后,可以查询信息,并进行导出(PDF或Exel)

大三上_javafx项目_第5张图片

选择帮助,点击“软件操作指南”

自动弹出帮助文档供用户查看

大三上_javafx项目_第6张图片

2、管理员

使用管理员账号登录,可进行后台管理

大三上_javafx项目_第7张图片

其他模块不再展示,这里展示一下图片管理模块

大三上_javafx项目_第8张图片

编辑窗口如下所示

这里管理的就是用户界面显示的那张图片

点击添加点位则会在图片左上角生成一个点位,通过鼠标拖动可以将点位移到指定位置

已有的点位也可以通过鼠标拖动改变位置

如果不想保存修改,则点击重载图片按钮,反之则点击保存按钮

在这里插入图片描述

点击“改变点位大小”按钮可以改变点位大小,单位为像素

大三上_javafx项目_第9张图片

导入功能与编辑功能差不多,这里不再演示

点击改变点位样式按钮,可以改变点位的样式(比如正常的点位用绿色圆圈,异常点位用红色圆圈)

大三上_javafx项目_第10张图片

二、过程

其实做桌面软件的话最好是用C#,但由于我和学长之前都没学过C#,而且甲方说这个项目以后可能会推出网页版,用Java的话以后改成网页版比较方便,所以我们是选择了Java来做。

确定好语言后,由于我们俩之前也没接触过JavaFX,所以我们找了如下资料来学习JavaFX

JavaFX下载

JavaFX 程序退出时结束子线程

JavaFX 非Parent的Node只能真实地加在最后一个Parent中

javaGUI的替代者JavaFX

JavaFX入门(二):JavaFX和FXML

javaFX开发环境配置

Java: JavaFX桌面GUI开发

JavaFX中文资料

JavaFX教程

JavaFX 15官网

JavaFX教程

用maven创建javafx项目 解决“错误: 缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序”

jdk8版本以上的javafx安装操作,通过下载javafx安装包,内附jdk8的安装包

1、前后端交互

在开始做项目之前,我先写了一个简单的Demo来搞明白JavaFX究竟如何使用。

由于我们俩对做Web项目比较熟悉,所以我们准备使用JavaFX中的Webview内嵌HTML页面来开发

1)第一个页面

首先需要搞明白的就是如何进行前后端交互

javaFX与js交互

JAVAFX应用程序嵌入本地的html文件(webview)

参考了这两篇博客后我明白了具体流程

要想实现内嵌HTML开发,需要写如下三个文件

Main

public class TestMain extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    @Override
    public void start(final Stage primaryStage) throws IOException {
    	Pane root = FXMLLoader.load(getClass().getResource("main.fxml"));
		

        primaryStage.setScene(new Scene(root, 500, 400));
        primaryStage.show();
    }

}

fxml








<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.BaseController">
    <children>
        <WebView fx:id="webView" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
    children>
AnchorPane>

Controller

public class BaseController implements Initializable {

	public static final Window stage = null;

	private  JSObject win;
	
	@FXML
	private WebView webView;
	
	private WebEngine webengine;
	
	
	@Override
	public void initialize(URL location, ResourceBundle resources) {
		
		// TODO Auto-generated method stub
		webengine= webView.getEngine();
		
	     webengine.getLoadWorker().stateProperty().addListener(
	                (ObservableValue<? extends Worker.State> ov, State oldState, Worker.State newState) -> {
	                    if (newState == State.SUCCEEDED) {
	                        win = (JSObject) webengine.executeScript("window");
	                        win.setMember("app", new JavaApp(webengine));
	                    }
	                });
	     System.out.println("初始化");
		String Url = this.getClass().getResource("test.html").toExternalForm();
		webengine.load(Url);
	}
	

}

具体过程就是Main函数中利用如下代码加载fxml文件
Pane root = FXMLLoader.load(getClass().getResource("main.fxml"));

然后fxml文件中会指定一个controller文件
fx:controller="application.BaseController

最后在controller文件中把html文件加载到webview中即可

	String Url = this.getClass().getResource("test.html").toExternalForm();
	webengine.load(Url);

而前后端的交互就是通过以下代码在HTML文件中增加Java对象来实现

	win = (JSObject) webengine.executeScript("window");
    win.setMember("app", new JavaApp(webengine));

前端如果想要调用后端的函数,只需在JavaScript函数中使用这个Java对象的函数即可。

格式为

app.函数()

2)切换页面

参考了如下博客

JavaFX多个界面中的数据传递

我们可以在JavaApp(上面那个用来与前端交互的类)中加一个函数

public void turnto(String url) {
    	System.out.println(url);
    	String Url = Main.class.getResource(url).toExternalForm();
		webengine.load(Url);
    }

需要跳转时,只需在javascript中使用 app.turnto(url)即可,比如

app.turnto("/WEB-INF/views/admin/adminMenu.html");

2、项目架构的搭建

1)项目文件目录

在搭建项目架构时,我想尽可能地利用上一个项目的经验,所以使用了SM框架(SpringMVC肯定是用不了的)

SM整合(spring,mybatis)

接下来我像写SSM项目时一样,建了entity(实体类)、dao(与数据库进行交互)、service(业务逻辑)包。

接下来就是controller(与前端交互)层了,前面三个包和之前完全没区别,但controller层显然不能和之前一样。

不过了解了JavaFX如何与前端交互后,这一层也不难写,只需多写几个JavaApp类即可。

为了与上面的BaseController区分,我将controller层的包命名为controllerUtils

最后项目的架构如下所示

src里是Java文件,resource里是html、js 、css文件

在这里插入图片描述

src架构如下。

大三上_javafx项目_第11张图片

entity、dao、service、controllerUtils上面说过了
dto、tools、 utils 就是一些工具类
timer类用来进行实时刷新(后面会详细说)
config、config.mybatis、config.spring就是配置文件
config.mybatis.mapper就是写SQL的mapper映射文件(XML文件)

main和controller包里就是实现内嵌HTML的那三个文件,
只有BaseController和上面不太一样

首先就是spring配置文件需要手动导入

context = new ClassPathXmlApplicationContext("classpath:config/spring/applicationContext.xml");//context为org.springframework.context.ApplicationContext类

然后就是把JavaAPP换成了controllerUtils包里的那些类

	win.setMember("app", new LoginUtil(context, win, webView, webengine, user));
    win.setMember("normal", new NormalUserUtil(context,  win, webView, webengine, user));
	win.setMember("tiaoshi", new TiaoshiUtil(context,  win, webView, webengine, user));
	win.setMember("admin", new AdminUtil(context,  win, webView, webengine, user));

2)记录当前用户

之前是用Session记录当前用户,现在只能是使用静态变量了(登录后给静态变量currentUser赋值,退出后将该静态变量置为null)

3)自动编译

之前遇到的另一个小问题:有时更改了代码后没有效果,看了下面的博客后解决了该问题

eclipse不能自动编译生成class文件的解决办法

3、文件问题

1)文件选择与上传

做web项目是使用html里的标签就行,但这个项目使用html标签的话无法与后端交互

这个问题困扰了我很久,甚至让我一度想放弃使用javafx,最后我想到了可以用javafx自带的文件选择器

点击按钮时触发Java的函数

<button type="button" onclick="admin.chooseFile()" name="file">选择文件button>

然后通过以下函数

选择文件:

public void chooseFile() {
		FileChooser fileChooser = new FileChooser();
        fileChooser.getExtensionFilters().addAll(
                new FileChooser.ExtensionFilter("All", "*.*"),
                new FileChooser.ExtensionFilter("JPG", "*.jpg"),
                new FileChooser.ExtensionFilter("PNG", "*.png")
            );
        file = fileChooser.showOpenDialog(stage);
        System.out.println(file);
	}

选择文件夹:

public void chooseFile() {
		DirectoryChooser directoryChooser = new DirectoryChooser();
        file = directoryChooser.showDialog(stage);
        //System.out.println(file);
	}

file即一个静态变量

得到选择的文件(静态变量file)后即可进行文件的上传了

最近学习了NIO,为了进行实践,我把文件上传函数中的文件复制部分改成了如下的样子(虽然效率提升不大,但聊胜于无)

(缓存、直接缓存、通道)

		FileChannel inchannel=FileChannel.open(Paths.get(mfile.toURI()), StandardOpenOption.READ);
		FileChannel outchannel=FileChannel.open(Paths.get(path), StandardOpenOption.WRITE,StandardOpenOption.CREATE_NEW);
		
		inchannel.transferTo(0, inchannel.size(), outchannel);

另一个问题就是最开始的时候,上传文件后需要手动刷新项目才能查看文件,查看了博客后解决了该问题

解决springboot上传文件至当前项目目录下,上传成功后,再次刷新项目才显示上传结果

2)导出文件

之前那个项目做过导出Exel,这次是导出PDF,其实也没多大区别:Java iText导出pdf功能实现

导出文件的时候遇到了一个让我挺无语的BUG,就是它会提示“指定的设备名无效”,看了下面的博客后才知道,给文件取名时有些名字是不能取的
win7下创建名为aux.c的文件提示“指定的设备名无效”

比如com3这个名字就不能取

大三上_javafx项目_第12张图片

3)打开本地文件

做项目时有一个需求是用户点击按钮,系统直接打开某个文件(不是点击下载),看了如下博客解决了该问题

使用java打开本地文件的方法

4、实时刷新及优化

实时检测系统,顾名思义就是可以实时地监测,一旦出了问题可以立即监测到

那如何实现呢?

一开始我和学长采用的方法是使用Javascript中的setInterval函数

在html页面实时显示系统时间

JS setInterval()/setTimeout()——实现动态时间,倒计时

setTimeout()、setInterval()不起作用?答案在这里

使用方法很简单:

setInterval(函数名,时间间隔) //时间间隔的单位为毫秒

但后来我们发现,用多了以后界面会非常卡

毕竟每次刷新都得从前端调用controllerUtils中的函数,然后controllerUtils调用service层,service层调用Dao层,Dao层查询数据库,最后再把数据一层层地返回回去,(尤其是涉及到数据解析的部分,还得从串口服务器读取数据,然后再解析,然后把解析出来的数据写入数据库)这么长的流程(而且还涉及到IO流操作)自然会很卡。

于是我和学长对此展开了讨论

学长说可以在后端写一个定时器,实时更新数据库(即把数据解析部分的实时刷新放到后端),然后前端再实时地读取数据库中的内容。

我在此基础上提出了另一个方案:把每一个需要实时刷新的数据存到controllerUtils中对应的一个静态变量中,后端用定时器实时更新这些静态变量,然后前端每次读取这些静态变量即可。

学长同意了这个方案,于是接下来的问题就是如何写定时器了

【简单定时器】JAVA 简单定时器的三种方法

我们选用了其中的第三种方法。

	ScheduledExecutorService service=Executors.newSingleThreadScheduledExecutor();
	service.scheduleAtFixedRate(runnable, 0, 1, TimeUnit.SECONDS);

第一行就是创建并管理一个只有一个线程的线程池

ScheduledExecutorService service=Executors.newSingleThreadScheduledExecutor();

第二行就是定时

service.scheduleAtFixedRate(runnable, 0, 1, TimeUnit.SECONDS);

第一个参数为运行的任务,第二个为延迟的时间,第三个为间隔的时间,第四个为时间单位

5、打包

每次迭代完一个版本后,我们就需要将项目打包成一个可运行软件发给甲方。

一开始是最笨的方法:让甲方安装JDK和Mysql

后来我们通过查看下面的博客学会了如何打包,如何在没有JDK的电脑上运行。

然后我们把Mysql换成了sqlite(一种轻量级数据库,可以理解为项目中内置一个数据库),解决了这个问题

区别:

  • sqlite数据类型只有integer 、real(小数) 、blob、 text
  • sqlite自增要插入null,mysql是0或null都可
  • sqlite无函数,没有concat函数,模糊查询时要写成
    … like '%$ {value}%'或…like ‘%${name}%’

第二种写法的前提是函数的参数前加上了@Param(“name”),

public List<Baojing> queryAll(@Param("name")String name);

附:

输入参数:parameterType

1.类型为 简单类型(8个基本类型+String)
  	#{}、$ {}的区别
	a.
	#{任意值}
	$ {value} ,其中的标识符只能是value	
	
	b.# {}自动给String类型加上''  (自动类型转换)
  $ {} 原样输出,但是适合于 动态排序(动态字段)
select stuno,stuname,stuage  from student where stuname = #{value}
select stuno,stuname,stuage  from student where stuname = '$ {value}
动态排序:
select stuno,stuname,stuage  from student  order by $ {value} asc

	c.# {}可以防止SQL注入
  $ {}不防止
  
$ {}、#{}相同之处:
	a.都可以 获取对象的值 (嵌套类型对象)
		i.获取对象值:
模糊查询,方式一:
select stuno,stuname,stuage  from student where stuage= #{stuAge}  or stuname like #{stuName} 
			Student student = new Student();
 			student.setStuAge(24);
 			student.setStuName("%w%");
 			List< Student> students = studentMapper.queryStudentBystuageOrstuName(student) ;//接口的方法->SQL
模糊查询,方式二:
	student.setStuName("w");
	select stuno,stuname,stuage  from student where stuage= #{stuAge}  or stuname like '%${stuName}%'
		ii.嵌套类型对象

2.对象类型
#{属性名}
${属性名}

1)如何打包

Java Swing实用小工具开发

javafx项目打包

使用exe4j打包javafx项目

jdk11使用jlink定制精简jre

jdk11订制jre + JavaFX11打包exe可执行程序

eclipse导出可执行的jar文件

jar导出与制作成exe在没jdk电脑下运行(图文教程+工具)

exe4j 将jar包封装为exe

甲方那边是Win7系统,为了测试他们的机器能否运行,我在虚拟机安了一个Win7系统

VMware 15 虚拟机安装 win 7 系统

打包后文件目录如下:

大三上_javafx项目_第13张图片

db为数据库文件(.db格式),jre8即运行环境,加上这个后没有jdk的电脑也可以运行。resource即项目涉及到的图片

其实还可以在此基础上再打包成可安装程序,不过我们目前还只是测试阶段,就没有进行再次打包

2)打包后配置文件的路径问题

jsp连接sqlite、Sqlite相对路径绝对路径问题

Spring配置文件打包到jar中无法加载问题之解决方案

使用exe4j把jar转换成exe文件时,报错java.lang.NoClassDefFoundError:

3)打包后上传、显示图片

参考博客:

Java将二进制流转Base64字符串并在页面显示(附Base64转二进制流)

解决Eclipse中无法直接使用sun.misc.BASE64Encoder及sun.misc.BASE64Decoder的问题

如何用JAVA将二进制文件转换成BASE64格式保存到MySQL的Blob字段里并读出下载

HTML base64格式的二进制流在 img 标签内显示

(1)显示图片

显示图片有很多种方法

①直接把img标签中的src写成相对路径即可显示

②先在后端利用反射加载classpath下的图片

this.getClass().getClassLoader().getResource(图片的相对路径)//返回URL

或者用

this.getClass().getResourceAsStream(图片相对路径)//返回InputStream

也可以用System.getProperty("user.dir") 获取项目的真实路径,然后在此基础上获取resource文件夹中的图片(考虑到之后还得上传图片,我采取的是这种方式)

然后把图片流转成base64格式的二进制流

	public String getImageByPath(String path) throws Exception {
    	File file=new File(path);
    	System.out.println("file:"+file.toString());
		FileInputStream fin = new FileInputStream(file);
		
		byte[] buffer = new byte[(int)file.length()];
		
		fin.read(buffer);
		
		String s=new BASE64Encoder().encode(buffer);  //将文件内容编码成base64格式后以字符串的方式保存到Blob字段中
			
		fin.close();
		
		return s;
	
    }

最后把该二进制流放到src里即可显示

document.getElementById("image").src="data:image/jpeg|png|gif;base64,"+app.getImageByPath(path);

③在数据库中直接存取图片二进制流,

然后用上面的方式显示。

但这样的话数据库查询效率会降低

(2)上传图片

打包后不能把图片上传到exe文件中,有以下两种解决方法(我想到的)

①直接把图片上传到数据库中(但数据库查询效率降低)

②打包后把用到的图片全放到与exe文件平行的resource文件夹里

大三上_javafx项目_第14张图片

可以这么做的原因是System.getProperty("user.dir") 这段代码比较神奇。

打包之前它获取的路径是项目的真实路径,打包之后它获取的是exe文件所在文件夹的路径

所以System.getProperty("user.dir")+"/resource/" 这段代码在在打包前可以正确获取到项目的resource文件夹中的图片,打包后也可以正确获取到与exe文件平行的resource文件夹中的图片

缺点就是每次打包都需要把项目中的resource文件夹中的图片复制到上面的resource文件夹中。(不影响正常使用)

6、写前端时遇到的问题

剩下的就是一些前端的问题,比如之前演示的拖动点位的实现

另外就是不知道为什么,写前端时JQuery有时好使有时不好使,EasyUI和LayUI中的控件也是有的好使有的不好使

我个人猜测这是因为JavaFX8使用的内嵌浏览器是非常落后的,对一些较新的前端技术不怎么支持

怎么让背景图片铺满整个页面

ajax 报错 400

用户在图片上点选并标记位置,js实现

FileReader.readAsDataURL()函数的使用

java流下载,前端ajax blob接收

window.URL.createObjectURL 的使用

解决EasyUi的combobox绑定change事件

layui+树结构表格+动态单元格加背景色

使用layer,layui,不能显示弹出效果

JavaScript数组方法

如何让两个div并排同行显示

table 每行 改变颜色

使用css实现一个圆形头像框效果

JS中String转int

实现div可拖放

关于js函数传参的问题

a标签href属性传递参数,onclick属性传递参数

form中的button问题

getElementsByClassName()的详细用法

Javascript改变css样式的四种方法

js点击οnclick=“函数”参数传入URL问题

a标签携带参数跳转并在跳转页面接收参数

Html代码中,< img src="">如何写图片路径

js刷新当前页面的5种方式

JS之String类型

js子级窗口相互调用父级的方法

向iframe传递数值简单方法(父页面向子页面传递数值)

iframe的跳转:直接改变iframe标签的src,如果需要传参,利用标签的参数传参

JSObject 的相关用法.

js获取单选按钮选项

关于z-index的详细解释

background-image 关于把一张图片完全显示在大div中,小div中

div标签常用属性

js获取图片宽高的方法

你可能感兴趣的:(项目,java,数据库,javafx,webview)