JavaFX8开发过程中的问题记录 青旅半醒 2022-08-04 15:45 435阅读 1赞 # JAVAFX8开发过程中的问题记录 # ## 起源 ## 上Java课的时候没有好好学,到后来自学一段时间才真正算是入门,不过听说Java不适合做桌面程序,所以对Swing这一块根本都没有看,而且也忽略了线程和文件IO那一块,到找工作和实际工作的时候才后悔莫及,不过幸好后来接触到的Android和J2EE慢慢地把这部分欠缺弥补回来了。听说有JavaFX这个东西的是在大三实训的时候,需要基于Java做一个类似局域网聊天工具的桌面程序,隔壁老师建议他带的几个小组使用JavaFX做界面,成果汇报的时候我惊呆了:这是个什么东西啊,按钮上的汉字居然不像Swing那样难看了,风格居然和bootstrap或者Mac OS有几分相似,从此对JavaFX有了一种特殊的关注和喜爱。 据说JAVAFX和当初的Applet都是为了和Flash一争高下,但是并不能。JAVAFX可以做出非常漂亮的RIA程序,于是我决定试一下。实训回来后我尝试着做一些程序,但是都是了解了一些皮毛,做了一个文本编辑器,连Swing JOptionPane那样的模态窗口都不知道如何实现,校招开始了,最后只能草草收场。毕设的时候用Java做声纹识别,打算界面用JavaFX做,结果后来觉得本来时间就不多,如果把那么多的时间花费在开发界面上有点不划算,所以又放弃了。如今JDK1.8都出了,基于JDK1.8的JavaFX8也出了,据了解到JavaFX已经过去一年多了,也工作了一段时间,和几个人合租,正好需要一个记录和结算集体支出的软件,灵机一动,奋战四个周末,终于完成,记录在开发过程中的一些问题以备后面查询。 ## 开发准备 ## \-工具:JDK1.8 + E(fx)clipse + Windows8 \-数据库:Access \-辅助设计:JavaFX Scene Builder 2.0 E(fx)clipse是专门针对JavaFX开发者设计的一个Eclipse版本,也可以用纯净的Eclipse安装JavaFX插件即可。 数据库使用Access主要是看中了他的移植性好,开销小,程序也不需要太复杂的存储和查询。 JavaFX Scene Builder可以通过拖动组件来设计界面并保存为fxml格式供程序使用,非常方便。 ## 数据库 ## 数据库设计非常简单,四张表:user、category、record、sumtime,分别用来储存用户、分类、记录和结算时间等信息。逻辑非常简单,登录后根据不同的身份分配权限,对支出记录做增删改查操作,结算时显示参与结算的用户应得或应出的金额就对了。 不过在调试数据库连接的时候出了一个大问题:以前使用ODBC的方式可以像这样连接Access数据库的 Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); String dbur1 = "jdbc:odbc:driver={Microsoft Access Driver (*.mdb)};DBQ=d://a1.mdb"; Connection conn = DriverManager.getConnection(dbur1, "username", "password"); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("select * from Table1"); while (rs.next()) { System.out.println(rs.getString(1)); } rs.close(); stmt.close(); conn.close(); 这个sun.jdbc.odbc.JdbcOdbcDriver也不要下载额外的jar,它就在jre/lib/rt.jar里面,但是JDK1.8之后Java不再支持ODBC连接,所以总会报ClassNotFoundException : sun.jdbc.odbc.JdbcOdbcDriver,最后找到了解决办法:通过JDBC方式连接,但是要下载第三方包Access\_JDBC30.jar,连接方式也要换成AccessDriver方式连接,一下子就高大上了 String strurl="jdbc:Access:///C:/x.mdb"; try { Class.forName("com.hxtt.sql.access.AccessDriver"); conn=DriverManager.getConnection(strurl); sta = conn.createStatement(); } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } 开发到中后期的时候,发现老是会报错:HXTT Access Version 5.1 For Evaluation Purpose allows executing not more than 50 queries once.查了下,原来这个包不是官方的,没有限制的版本是要给钱的,不给钱从AccessDriver加载开始,查询次数不能超过50次,据说单次查询结果还不能超过1000条。我找了找看看有没有替代的解决办法,发现JDK1.8 Access JDBC解决方案仅此一家,而不使用JDK1.8的后果就是不能使用JavaFX的新特性。难道要换Mysql?可是我找了破解版的jar,就继续用Accesss咯~ ## Hello World! ## package application; import javafx.application.Application; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.layout.BorderPane; public class Main extends Application { @Override public void start(Stage primaryStage) { try { BorderPane root = new BorderPane(); Scene scene = new Scene(root,400,400); scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); primaryStage.setScene(scene); primaryStage.show(); } catch(Exception e) { e.printStackTrace(); } } public static void main(String[] args) { launch(args); } } 这是用E(fx)clipse新建JavaFX应用自动生成的一个例子,运行结果如下图: ![Hello World!][Hello World] ## 主要界面和功能 ## 登录界面 ![登录界面][20150905160347474] 主界面 ![主界面][20150905160524362] 设置界面 ![设置界面-用户设置][-] ![设置界面-分类设置][- 1] ![设置界面-关于][- 2] 记账界面 ![记账界面][20150905161030800] 结算界面 ![结算界面][20150905161206183] ## 问题记录 ## ### 配置文件 ### 配置文件用properties文件记录,然而整个系统可配置的东西就是数据库路径,因此properties文件里面只有一个property:db.path,可以通过登录界面右上角的黄色按钮配置。其实这种小工具连数据库都可以不要,所有东西都用properties文件存储,不过这样风险很高,频繁操作文件效率也不高,当记录足够多的时候可能不太方便,这些都是我猜的,所以就还是用数据库存储吧。 ### 模态窗口 ### 很早之前为模态窗口发愁,以为JavaFX根本就不可能实现模态窗口,用Swing的时候直接使用JOptionPane就可以调用各种模态窗口,搜索一番并结合API,发现这样做就可以实现模态窗口 public class Add{ private Stage stage; public Add(Stage stage) { this.stage = stage; final Stage primaryStage = new Stage(); primaryStage.initStyle(StageStyle.UNDECORATED); primaryStage.initModality(Modality.APPLICATION_MODAL); primaryStage.initOwner(stage); primaryStage.getIcons().add(new Image(getClass().getResourceAsStream( "greenorange.png" ))); String filename = "add.fxml"; Pane page; try { page = (Pane) FXMLLoader.load(getClass().getResource(filename)); Scene scene = new Scene(page); primaryStage.setScene(scene); primaryStage.show(); } catch (IOException e) { e.printStackTrace(); } } } 只要使用 primaryStage.initModality(Modality.APPLICATION_MODAL); primaryStage.initOwner(stage);//stage为父窗口stage 就能设置此窗体属性为模态,并且用new Add(*PARENT\_WINDOW\_STAGE*)调用时,将父窗口的stage作为参数传入就OK了 ### 去掉Windows默认窗体样式 ### 如果在Mac上使用JavaFx,窗体样式和content应该比较和谐,但是在windows上却不那么和谐了,在初始化Stage的时候调用 primaryStage.initStyle(StageStyle.UNDECORATED); 就可以去掉窗口,不过去掉之后窗口无法拖动、最大化和最小化,需要自己添加按钮实现。然而在实现拖动的时候又出了个大问题:OnDragged监听器在鼠标每次移动后会重新计算窗口的位置,导致窗口会先将左上角移动到鼠标位置之后再跟随鼠标移动,这个用户体验太不好了。最后找到原因:实现窗口拖拽需要监听两个事件,一个是鼠标按下时(OnMousePressed),另一个是鼠标拖拽时(OnMouseDragged)。前者记录鼠标位置相对于stage的横纵坐标,后者用于重新定位。然而之前实现拖拽的时候鼠标按下用的OnMouseClicked,Press和Click的区别是,前者是点击下去还未弹起就触发,后者是弹起后再触发,因为拖拽过程中一直没有弹起,所以之前还未记录鼠标的初始位置就开始了拖拽,导致相对坐标始终为(0,0),当然先要对其左上角再拖动了,具体代码如下: public class Login extends Application{ private Pane animationPane; private Double stageDragInitialX; private Double stageDragInitialY; @Override public void start(Stage primaryStage) throws Exception { primaryStage.initStyle(StageStyle.UNDECORATED); primaryStage.getIcons().add(new Image(getClass().getResourceAsStream( "greenorange.png" ))); String filename = "login.fxml"; Pane page = (Pane) FXMLLoader.load(getClass().getResource(filename)); initControls(page);//初始化控件 addListener(primaryStage);//为控件添加监听器 Scene scene = new Scene(page); primaryStage.setScene(scene); primaryStage.show(); private void initControls(Pane page) { animationPane = (Pane) page.getChildren().get(0); } private void addListener(Stage primaryStage) { animationPane.setOnMouseDragged(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { primaryStage.setX(event.getScreenX() - stageDragInitialX); primaryStage.setY(event.getScreenY() - stageDragInitialY); } }); animationPane.setOnMousePressed(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { stageDragInitialX = event.getScreenX() - primaryStage.getX(); stageDragInitialY = event.getScreenY() - primaryStage.getY(); } }); } } ### TableView刷新 ### 程序里使用了很多次TableView,JavaFX的TableView不像Jquery-easyUI那样一行一行来加载的,而是通过一列一列来加载的,多用几次就知道该怎么做了。TableView的数据源是一个ObservableList,只要是Observable的属性,只要属性值改变,引用这个属性的对象也会立刻加载新的属性,所以TableView刷新不需要像Android ListView那样需要notify一下。但是按照这个原理有时候并不能正常刷新,比如要重新加载整个table,使用*OLD\_OBSERVABLE\_LIST* = *NEW\_OBSERVABLE\_LIST* 这样是不行的,即使再次使用TableView.setItems(*NEW\_OBSERVABLE\_LIST*)也不会刷新。我的解决办法是,如果新的数据集和就旧的数据集长度一样就使用FXCollection.copy()函数,或者先clear再一个个加进去,后者特别适用于长度不同的情况。最后我发现不是不刷新,而是数据集更新方式不对,直接在List层面赋值不会有任何效果,但是直接改动List里面的元素会刷新,前面的解决方法就是变态地更改List里面的元素,而且使用TableView.refresh()可以直接强制刷新,也就是说使用TableView.setItems(*NEW\_OBSERVABLE\_LIST*)之后再使用TableView.refresh()就可以强制刷新! ### CallBack函数 ### Javascript何以非常简单的实现callback功能,但是Java不能把method作为参数传递,因此回调函数一般通过实现接口的方法实现,之前从来没有尝试过callback函数,这次自定义了一个CallBack接口,定义一个callBack()函数,将不同的实现了CallBack接口的对象作为参数传入各种模态窗口,完成某一个操作时调用特定的CallBack接口的callBack()方法,这样模态窗口就成了一个正真公用的类,而不涉及具体业务逻辑。 ## 总结 ## \-没使用统一的权限存储管理,而是用代码根据用户名来判断用户权限。 \-很多Application的公用方法,比如初始化组件、添加监听器、拖拽功能等没有通过Override方式实现,比如可以写一个BaseApplication extends Application,在BaseApplication预定义或者初步实现一些方法,所有新的Application全部extends BaseApplication,通过覆盖父方法实现更丰富的功能。 \-公用方法提取粗糙,比如OKCancelDialog完全不用独立写一个方法,而是像JOptionPane一样通过静态方法调用,而且可以使用返回值,让更多的业务逻辑代码在业务类实现。 [Hello World]: /images/20220731/f3ea21b7dd8048ccaa3a9deda1c4a84d.png [20150905160347474]: /images/20220731/2af268445f5344a1aca6ded87b1d5835.png [20150905160524362]: /images/20220731/f7f6530ffb784899a982ccb97a846818.png [-]: /images/20220731/764655c2518d48c7beb56e30c0d27a30.png [- 1]: /images/20220731/8f6b2441570e4573a01dde2144936613.png [- 2]: /images/20220731/bea12b3bc2cc41faa9392e9d100db361.png [20150905161030800]: /images/20220731/cf84a7290772432f99385dbc569d51cb.png [20150905161206183]: /images/20220731/048c5de8f0d64082aa9a79519d4a8b2b.png
相关 JavaFX UI开发中常见的布局问题 在JavaFX UI开发中,常见的布局问题包括但不限于以下几点: 1. **空间填充**:如果一个控件很大或者需要占据更多的空间,但其他控件较小,可能会导致界面拥挤。 2. Love The Way You Lie/ 2024年10月21日 13:18/ 0 赞/ 42 阅读
相关 JavaFX GUI编程过程中出现的视图管理问题 在JavaFX GUI编程中,确实会出现一些视图管理的问题。以下是一些常见的问题以及解决方法: 1. **布局不清晰**:如果控件没有正确地排列在舞台上,就会导致布局混乱。使 小咪咪/ 2024年09月15日 17:39/ 0 赞/ 52 阅读
相关 javafx开发过程中遇到的问题汇总(持续更新) 1. 打开某个窗口是程序直接报错崩溃 > 原因可能是程序安装到了C盘, win系统对系统盘是有保护的, 必须使用管理员权限才能进行文件或文件夹的操作。因此,如果程序中出现 心已赠人/ 2023年10月15日 19:36/ 0 赞/ 9 阅读
相关 开发过程中规范说明与记录 开发过程中需要的文件 1. .gitignore 准备一份标准的.gitignore文件。 2. README.md 使用markdown的语 系统管理员/ 2023年08月17日 17:28/ 0 赞/ 178 阅读
相关 PySpider 使用过程中的问题记录 问题记录 PyCurl的问题 安装时出现: `Command python setup.py egg_info failed with error code ╰+哭是因爲堅強的太久メ/ 2023年06月06日 08:10/ 0 赞/ 4 阅读
相关 airflow使用过程中的问题记录 使用python校验dag脚本报错在这里插入代码片 python3 test.py 提示缺少模块或者直接报错的 重新安装python3,编译的时候看有没有依 「爱情、让人受尽委屈。」/ 2022年09月02日 01:17/ 0 赞/ 240 阅读
相关 暑期开发过程中的一些经验记录 [Jeremy Lin][] 一、GDI+设置 这个不算是什么经验,在学校里面主要应用Opencv,基本没用过GDI+,到公司后才发现需要用到它。这里记录一下GDI+的配置 骑猪看日落/ 2022年08月12日 01:59/ 0 赞/ 15 阅读
相关 JavaFX8开发过程中的问题记录 JAVAFX8开发过程中的问题记录 起源 上Java课的时候没有好好学,到后来自学一段时间才真正算是入门,不过听说Java不适合做桌面程序,所以对Swing这一块根 青旅半醒/ 2022年08月04日 15:45/ 1 赞/ 436 阅读
相关 记录jsp开发中的问题 1.在访问URI资源时,一定要写后缀名,否则找不到 2.在进行bootstrap开发时最好以官方教程为准,有些东西时效性太快 row-fluid和ro 青旅半醒/ 2022年06月11日 09:12/ 0 赞/ 268 阅读
相关 Mysql8 安装过程及安装过程系列问题记录 Mysql8 安装过程及安装过程系列问题记录 前言: 今天,想装个高版本一点的mysql试试,于是下载了一个mysql8的zip版本。 地址:https://dev. 朴灿烈づ我的快乐病毒、/ 2022年02月13日 02:43/ 0 赞/ 327 阅读