前言
在学习Swing后,听老师说使用Java写界面还可以使用JavaFX。课后,便去了解。JavaFX是甲骨文公司07年推出的期望应用于桌面开发领域的技术。在了解了这个技术几天后,便使用它完成Java课程的大作业一个日记系统。(因为前面有Swing的基础,所以入门JavaFX比较快)还需要说明,博主是使用SceneBuilder
配合JavaFX做的日记系统。
下面将介绍使用JavaFX完成日记系统的详细思路以及一些关键的步骤,完整工程代码会附在文末。
前期的入门准备
先介绍一下博主入门JavaFx的过程,希望可以对毫无基础的小伙伴有一个帮助。
博主先是花了两三天时间跟着B站上面一个Up主的视频入坑学习。现在网上认真讲JavaFX技术的资料不多,学的人也少,这个技术还有种局势有点不妙的感觉,于是这个Up主每次视频一开讲都说:“欢迎大家收看JavaFX没人看系列”,每次博主都会默默评论我在看我在看,这个Up主实在很有趣。但是有一点不好,博主觉得Up主的语速有点慢(可能是赶着做大作业比较心急吧),每次播放都是放2倍速,2倍速的语句听起来刚刚好。需要注意,一定要慢慢学不能急!
B站JavaFX技术学习视频链接:https://space.bilibili.com/5096022/channel/detail?cid=16953
前面提到过博主是使用SceneBuilder对界面进行布局,因为时间着急所以就没有写代码布局,而是使用这个工具。在正式开始写日记系统之前,看了一个使用SceneBuilder搭配做联系簿小项目的教程。若是也准备使用SceneBuilder完成界面布局也可以拿这个项目练手。
使用SceneBuilder搭配做联系簿项目教程:https://code.makery.ch/zh-cn/library/javafx-tutorial/
这个教程网站的站长是一个优秀的国外程序员,这个网站几乎全是关于JavaFX技术教程的网站,这个站长喜欢做教育所以网站上的教程都通俗易懂,很适合入门学习!
易百教程的JavaFX教程也不错,也有教使如何用SceneBuilder:https://www.yiibai.com/javafx/javafx-tutorial-for-beginners.html
日记系统的一个整体观感
日记实现的思路
日记系统的实现使用了MVC
的思想(默认大家都理解了MVC的思想),但是这里不是严格按照这种思想实现。看下面的工程目录树,了解整体的框架。
下面我们从主控制器开始讲解:
主控制器
在看下面之前列要求没有使用过JavaFX的小伙伴至少把易百教程的JavaFX快速入门看一遍并且敲一遍。
主控制器是控制所有视图之间的切换。每一个视图(登录、注册、忘记密码、写日记和查找日记)就是一个scene
,视图在主控制器的stage
中切换,使用stage.setScene(scene);
每切换到一个视图时就会创建对应的控制器并且将主控制器的引用传给该控制器对象,使得可以调用主控制器的方法切换到其他视图
当程序一启动显示的登录界面,但是按下注册按钮后,就会调用lgController.mainApp.showRegistView()
方法切换到注册视图。
public class MainApp extends Application {
//视图展现的“舞台”
private Stage stage;
//“场景”(视图)
private Scene scene;
//标识当前用户
private User user;
@Override
public void start(Stage primaryStage) {
try {
stage = primaryStage;
//显示登录界面
showLoginView();
//调用show方法显示
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
/**
* 显示登录界面
*/
public void showLoginView() {
try {
stage.setTitle("Login");
stage.getIcons().clear();
stage.getIcons().add(new Image("file:images/login.png"));
//创建登录控制器对象
LoginViewController lgController = (LoginViewController)replaceSceneContent("login/LoginView.fxml");
//将主控制器的引用传给登录控制器对象
lgController.setMainApp(this);
}catch(Exception e) {
e.printStackTrace();
}
}
/**
* 显示注册界面
*/
public void showRegistView() {
try {
stage.setTitle("Regist");
stage.getIcons().clear();
stage.getIcons().add(new Image("file:images/regist.png"));
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("regist/RegistView.fxml"));
BorderPane bp = (BorderPane)loader.load();
scene = new Scene(bp);
stage.setScene(scene);
stage.setResizable(false);
RegistViewController regController = (RegistViewController)loader.getController();
System.out.println(regController);
regController.setMainApp(this);
}catch(Exception e) {
e.printStackTrace();
}
}
//省略显示其他视图的方法......
/**
* 显示指定的视图
*/
private Object replaceSceneContent(String fxmlFile) {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource(fxmlFile));
AnchorPane ap = null;
try {
ap = (AnchorPane)loader.load();
}catch(IOException e) {
e.printStackTrace();
}
scene = new Scene(ap);
stage.setScene(scene);
stage.setResizable(false);
return loader.getController();
}
public static void main(String[] args) {
launch(args);
}
}
登录
以登录为例子介绍一下如何使用SceneBuilder与JavaFX结合。SceneBuilder是需要安装的,没有安装的小伙伴请看下易百的教程。
创建LoginView.fmxl文件(SceneBuilder构造界面产生的内容将写在该文件里),右击该文件选择open with SceneBuilder。
在创建好LoginView.fxml文件后要在相同包下创建控制器,不然在SceneBuilder设置该视图控制器时会找不到控制器类。
SceneBuilder构造视图
在布局完后,是可以使用Preview预览功能先查看效果
控制器LoginViewController.java
与视图中关联的组件和方法要一律使用@FXML注解
public class LoginViewController {
//对主控制器的引用
private MainApp mainApp;
@FXML
private Label userNameLabel;
@FXML
private Label passwordLabel;
@FXML
private Button LoginButton;
@FXML
private Button registButton;
@FXML
private ImageView leftImageView;
@FXML
private TextField userNameField;
@FXML
private TextField passwordField;
@FXML
private Label errorInfoLabel;
/**
* 获取主控制器的引用
*/
public void setMainApp(MainApp mainApp) {
this.mainApp = mainApp;
}
//在fxml文件被加载后自动调用
@FXML
private void initialize() {
//设置用户名输入框和密码输入框前的图标、左边图片
userNameLabel.setGraphic(new ImageView(new Image("file:images/user.png")));
passwordLabel.setGraphic(new ImageView(new Image("file:images/password.png")));
leftImageView.setImage(new Image("file:images/leftimage.png"));
}
/**
* 处理登录 到主界面的事件
*/
@FXML
private void handleLoginButtonAction() {
// ... 对用户名和密码的判断、跳转主视图
}
/**
* 处理注册按钮事件
*/
@FXML
private void handleRegistButtonAction() {
//显示注册视图
mainApp.showRegistView();
}
/**
* 处理忘记密码事件
*/
@FXML
private void handleForgetPasswordAction() {
//显示忘记密码视图
mainApp.showForgetView();
}
}
登录功能的实现思路
-
在SceneBuilder中完成组件布局
-
在相同的包下面创建控制器,控制器中需要使用@FXML注解与视图中组件相关联的组件对象,如这里的LoginButton、registButton等。我们在控制器中对这些被注解的组件操作,就是对视图中的组件操作。
在控制器中使用@FXML注解的方法,是可以作为事件触发时的处理方法。
-
右击fxml文件,选择open with SceneBuilder,设置视图的控制器、关联组件以及组件相应事件处理该调用的处理方法。
-
每次在SceneBuilder中修改完fxml文件退出后,都应该刷新工程。因为改变可能不会立即生效。
这就是博主实现登录功能的一个思路,后面每个功能的基础实现差不多都是这样:先布局然后写控制器再关联。登录功能要处理的内容很少,就只有用户名和密码的合法性校验,因此对具体实现不多介绍(文末项目源代码中有详细过程)。
后面对其他功能的介绍主要侧重在于该功能的作用和做该功能时博主遇到的难点。
注册
注册主要涉及到对用户输入合法性的检测。注册成功后可以直接跳转主视图或者登录视图。
忘记密码
忘记密码的实现也很简单,先是用户输入用户名,若用户名存在就可以跳转密保问题回答视图,否则弹出提示框该用户名不存在。
在密保问题视图,若是回答正确密保问题就可以跳转重置密码视图,否则弹出提示框答案错误。
若重置的密码符合密码要求规范,就重置成功跳转登录视图,否则重新输入重置的密码。
主视图
主视图主要起一个选择菜单的作用,让当前登录用户选择是写日记还是登录日记。
写日记
实现写日记时使用了两个有趣的组件:DatePicker和HTMLEditor。DatePicker可以弹出日期供用户选择,使用方便。
HTMLEditor是使用在Web的富文本编辑器,这里博主使用这个组件来使用户编辑日记格式更加丰富一点。
查找日记
在左侧列表显示出所有日记,在列表中选中相应日记后,右边的详细信息会显示选中日记的详细内容。在点击编辑按钮后,右边详情区便可以被修改和更新。上方还设置了根据日记的标题进行查找日记,因为根据其他关键字搜索日记都是大同小异所以仅实现了依据标题查找。
模型类
Diary类和User类的对象就是数据库中的日记和用户表的实体。
工具类
CheckValidTool类
在各类中使用正则对用户名、密码以及邮箱的合法性检查
DialogTool类
因为在用户输入或者进行提交等操作时,会出现错误警告等信息,需要弹出提示框进行提醒。若在控制器中每一处可能会出错的地方都写提示框代码就会显得代码臃肿,于是将需要用到了提示框写成一个工具类方便调用。
VerificationCodeTool类
这是一个算术运算验证码的工具类,负责产生一个三元四则算术运算验证码。实现比较简单。可以贴代码看一下:
public class VerificationCodeTool {
/**
* 生成四则算术验证码
*
* @return Map<String,Integer> 生成的四则运算验证码字符串和正确的结果
*/
public static Map<String,Integer> generateArithmeticVerification() {
//生成三个随机操作数
Random random = new Random();
int a = random.nextInt(10)+1;
int b = random.nextInt(10)+1;
int c = random.nextInt(10)+1;
//从'+', '-', '*', '/'随机选择两个运算符
char[] OperatorArray = new char[] {'+', '-', '*', '/'};
int opIndex1 = random.nextInt(4);
int opIindx2 = random.nextInt(4);
char op1 = OperatorArray[opIndex1];
char op2 = OperatorArray[opIindx2];
int result = 0; //算术运算的真实结果
if(comparisonOpPriority(op1, op2)) {
result = calculate(a, b, op1);
result = calculate(result, c, op2);
}else {
result = calculate(b, c, op2);
result = calculate(a, result, op1);
}
//System.out.print("请输入"+ a + op1+ b + op2 + c + "=?的答案:" ); //提示用户输入验证码的答案
String opExp = ""+a + op1+ b + op2 + c ;
Map<String,Integer> resExp = new HashMap<>();
resExp.put(opExp, result);
return resExp;
}
/**
* 二元一则运算
* @param a 第一个操作数
* @param b 第二个操作数
* @param op 操作符
* @return int 运算结果
*/
public static int calculate(int a, int b, char op) {
int calResult = 0;
switch(op)
{
case '+':
calResult = a+b;
break;
case '-':
calResult = a-b;
break;
case '*':
calResult = a*b;
break;
case '/':
calResult = a/b;
break;
}
return calResult;
}
/**
* 比较操作符的优先级
* @param op1 第一个操作符
* @param op2 第二个操作符
* @return boolean
*/
public static boolean comparisonOpPriority(char op1, char op2)
{
boolean op1HasHighPriority = true;
switch(op1) //若是op1和op2为/或者*时,默认op1的优先级高
{
case '-':
case '+':
if(op2=='*' || op2=='/')
op1HasHighPriority = false;
break;
}
return op1HasHighPriority;
}
}
JDBCTool类
数据库连接工具类。与数据库交互时使用的是PreparedStatement
语句。连接数据库是使用Properties配置文件方式。操作数据库时时刻需要注意的就是使用完资源后要记得释放资源!
小结
了解JavaFX以及使用其完成日记系统差不多花了一个星期多一点,其中主要靠使用SceneBuilder完成界面布局才使得方便许多。博主其实觉得JavaFX和Qt很相似,一个是Java一个是C++用来写界面的只是语言种类不同。虽然这个日记系统真的简单,但是使用新的技术做出这个小成品还是满开心的。在做完这个小成品后,我觉得以后接触新知识需要注意以下两点:
-
官方文档真的很重要
-
看视频教学不要只看一定要跟着做(不管视频中的示例多么简单)或者看完后独立实现一次
而且官方网站也有教程的(但是是英文的),不要局限于别人博客中的教程,多看官网教程
-
学习要慢,不能急
可能这些注意点很多人已经提过了,但是毕竟是博主亲身体会过,还是有必要总结记下来,在学习之路上增加一点经验值。
JavaFX技术博主还是了解的很肤浅,也还在学习,有问题可以留言我们一起交流~