JavaFX8开发过程中的问题记录

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!

主要界面和功能

登录界面
登录界面

主界面
主界面

设置界面
设置界面-用户设置
设置界面-分类设置
设置界面-关于

记账界面
记账界面

结算界面
结算界面

问题记录

配置文件

配置文件用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一样通过静态方法调用,而且可以使用返回值,让更多的业务逻辑代码在业务类实现。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
JavaFX是一个用于构建富客户端应用程序的Java框架,可以用来构建聊天小程序。以下是一个简单的JavaFX聊天小程序的实现步骤: 1. 创建JavaFX应用程序 首先,需要创建一个JavaFX应用程序,并设置主舞台的大小、标题等属性。可以使用Scene Builder等工具来设计用户界面。 2. 添加用户界面组件 在用户界面添加聊天窗口、输入框、发送按钮等组件,以方便用户进行聊天。 3. 实现Socket通信 使用Java Socket API实现与服务器的通信,以接收和发送聊天消息。可以使用Java的多线程机制,将Socket通信和UI操作分别运行在不同的线程,以避免阻塞UI线程。 4. 处理聊天消息 接收到消息后,需要将消息显示在聊天窗口。可以使用JavaFX的ListView组件来实现聊天记录的显示。 5. 实现发送消息 当用户在输入框输入消息并点击发送按钮时,应该将消息发送给服务器,并在本地保存聊天记录。发送消息后,应该清空输入框以便用户继续输入。 6. 错误处理和异常处理 在Socket通信可能会出现各种异常情况,例如网络连接错误、消息格式错误等。应该对这些异常情况进行适当的处理,以保证程序的稳定性和用户体验。 以上就是使用JavaFX实现聊天小程序的基本步骤。当然,实际的开发过程还需要考虑很多细节问题,例如用户登录、好友列表、消息加密等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值