前言
在學習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技術博主還是了解的很膚淺,也還在學習,有問題可以留言我們一起交流~
