JavaFX入门笔记

2023-02-12,,

JavaFX入门笔记

背景

Java选修课第四次实验

所需工具

IDEA
JavaFX插件(需要Maven)
JavaFX Scene Builder

参考资料

https://www.yiibai.com/javafx/javafx-tutorial-for-beginners.html
https://cloud.tencent.com/developer/article/2171652

开始操作

安装JavaFX插件

File -> Settings -> Plugins中搜索javafx然后下载第一个

之后便可以在IDEA中创建JavaFX项目

配置JavaFX Scene Builder

https://www.oracle.com/java/technologies/javafxscenebuilder-1x-archive-downloads.html 从网址下载适合自己电脑的版本

点击运行进行安装

点击下一步自行安装即可

安装完毕后电脑桌面会出现如图图标

创建项目

此处放上最终项目目录

设计界面

    先对自动生成的.fxml文件和启动类改名,此处分别改为mainWindow和MainWindow
    右键mainWindow.fxml并选择在SceneBuilder打开

    设计界面

    进行相关绑定

在IDEA中打开mainWindow.fxml

注意:给控件设置id时要写fx:id="",不能只写id="",否则将无法绑定!!!

编写DAO包中的类(实现文件读写操作)

CustomerDAO.java源码:

package banking.DAO;

import javafx.util.Pair;

import java.io.IOException;
import java.io.RandomAccessFile; public class CustomerDAO { //Specify the size of two string fields in the record
final static int FIRST_NAME_SIZE = 16;//FirstName最大长度(也是存储长度,不足则用空格不足)
final static int LAST_NAME_SIZE = 16;//LastName最大长度
public final static int CUSTOMER_SIZE = (FIRST_NAME_SIZE + LAST_NAME_SIZE);//每份数据的最大长度 //写入用户数据
public static void writeCustomr(String first, String last, RandomAccessFile raf) {
try {
raf.seek(raf.length());//在文件尾部进行写操作
FixedLengthStringIO.writeFixedLengthString(
first, FIRST_NAME_SIZE, raf
);//写入firstname
FixedLengthStringIO.writeFixedLengthString(
last, LAST_NAME_SIZE, raf
);//写入lastname
} catch (IOException e) {
e.printStackTrace();
}
} //在指定位置写入用户数据
public static void writeCustomer(String first, String last, RandomAccessFile raf, long position) {
try {
raf.seek(position);//前往特定位置进行写操作
FixedLengthStringIO.writeFixedLengthString(
first, FIRST_NAME_SIZE, raf
);//写入firstname
FixedLengthStringIO.writeFixedLengthString(
last, LAST_NAME_SIZE, raf
);//写入lastname
} catch (IOException e) {
e.printStackTrace();
}
} //读出用户数据
public static Pair readCustomer(long position, RandomAccessFile raf) throws IOException{
raf.seek(position);//前往特定位置进行读操作 String first = FixedLengthStringIO.readFixedLengthString(
FIRST_NAME_SIZE, raf
); //读出firstname
String last = FixedLengthStringIO.readFixedLengthString(
LAST_NAME_SIZE, raf
); //读出lastname //返回一个记录姓名的键值对
return new Pair<String, String>(first, last);
}
}

编写控制类(实现各自操作)

Add:将用户输入的客户名添加进文件中并在List显示
Del:将用户输入的客户名在文件中删除并更新List;若输入的客户名不存在则提示删除失败
Search:查找用户输入的客户名,若存在则会在List中置顶;若不存在则提示查找失败
Sort:将现有数据按照姓名进行排序并在List更新

package banking.controller;

import java.io.IOException;
import java.io.RandomAccessFile; import java.net.URL;
import java.util.*; import banking.DAO.CustomerDAO;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.util.Pair; public class CustomersController implements Initializable{
//对以下几个成员变量进行@FXML注入,注意变量名与fxml文件中的fx:id对应相同
//添加@FXML注解表明与fxml文件进行绑定
@FXML
private ListView customersList;
@FXML
private TextField firstName;
@FXML
private TextField lastName; // Access customers.dat using RandomAccessFile
private RandomAccessFile raf; // A list which stores customers' name
List<String>names = new ArrayList<>(); //重写初始化函数,类似于构造函数
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
// Open or create a random access file
try {
raf = new RandomAccessFile("customers.dat", "rw");
}
catch(IOException ex) {
System.out.print("Error: " + ex);
System.exit(0);
} //设置ListView不可编辑
customersList.setEditable(false); //将文件中原先存有的数据读出并展示
try {
getCustomersFromRAF();
} catch (IOException e) {
e.printStackTrace();
} } //add事件
//添加@FXML注解表明与fxml文件进行绑定
@FXML
protected void onAddButtonClick() throws IOException {
//调用接口写入客户名
CustomerDAO.writeCustomer(firstName.getText(), lastName.getText(), raf);
getCustomersFromRAF();//读出数据并展示
} //del事件
//添加@FXML注解表明与fxml文件进行绑定
@FXML
protected void onDelButtonClick() throws IOException {
//获取用户输入的客户名
String target = clearSpaces(firstName.getText() + ',' + lastName.getText());
if (names.contains(target)) {//若该客户名存在
//重新读取文件内容
long currentPosition = 0;//定位指针
names.clear();//清空列表,准备重新读入
while (currentPosition < raf.length()) {
//读出客户名,存在Pair对象中
Pair<String, String>customer = CustomerDAO.readCustomer(currentPosition, raf);
//对读出的客户名进行拼接操作
String name = clearSpaces(customer.getKey()) + ',' + clearSpaces(customer.getValue());
// System.out.println(name);
names.add(name);//在客户名列表中新增 //自增(偏移量)
//注意要2*CustomerDAO.CUSTOMER_SIZE
//具体原因不清楚,但.dat文件中每条数据的长度为2 * CustomerDAO.CUSTOMER_SIZE
//因此偏移量为2 * CustomerDAO.CUSTOMER_SIZE而不是CustomerDAO.CUSTOMER_SIZE
currentPosition += 2 * CustomerDAO.CUSTOMER_SIZE;
} //用最后一条数据覆盖要删除的数据
String[]name = names.get(names.size()-1).split(",");//获取最后一条数据并存在字符串数组中
//写入位置names.indexOf(target) * 2 * CustomerDAO.CUSTOMER_SIZE
//解读:
//names.indexOf(target)为要删除的数据在客户名列表中对应的索引,也为在文件中的索引
//将此索引乘以偏移量,即为此条数据在文件中的具体位置,然后进行写入操作即可覆盖
CustomerDAO.writeCustomer(name[0], name[1], raf,names.indexOf(target) * 2 * CustomerDAO.CUSTOMER_SIZE);
raf.setLength(raf.length() - 2 * CustomerDAO.CUSTOMER_SIZE);//重置raf数据长度(删除最后一条数据) //重新展示
getCustomersFromRAF();
} else {
//新建一个对话框提示删除失败
Dialog<ButtonType> warning = new Dialog<>();
warning.getDialogPane().getButtonTypes().add(new ButtonType("确认", ButtonBar.ButtonData.OK_DONE));
warning.setTitle("删除失败");
warning.setContentText("该客户不存在");
warning.show();
} } //search事件
//添加@FXML注解表明与fxml文件进行绑定
@FXML
protected void onSearchButtonClick() {
//查找该元素并交换位置
String target = clearSpaces(firstName.getText() + ',' + lastName.getText());
if (names.contains(target)) {//若要查找的客户名存在
Collections.swap(names, 0, names.indexOf(target));//则将其置顶,即放在names中第一个元素的位置
//将names的数据放入ListView中
ObservableList<String>items = FXCollections.observableArrayList(names);
customersList.setItems(items);
} else {
//新建一个对话框提示查找失败
Dialog<ButtonType> warning = new Dialog<>();
warning.getDialogPane().getButtonTypes().add(new ButtonType("确认", ButtonBar.ButtonData.OK_DONE));
warning.setTitle("查找失败");
warning.setContentText("该客户不存在");
warning.show();
}
} //sort事件
//添加@FXML注解表明与fxml文件进行绑定
@FXML
protected void onSortButtonClick() {
//对names进行排序
names.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
String[] name1 = o1.split(",");
String[] name2 = o2.split(",");
if (name1[1].equals(name2[1])) {
return name1[0].compareTo(name2[0]);
} else {
return name1[1].compareTo(name2[1]);
}
}
});
//将排序后的names的数据放入ListView中
ObservableList<String>items = FXCollections.observableArrayList(names);
customersList.setItems(items);
} //从文件中读取数据并存入names然后展示
protected void getCustomersFromRAF() throws IOException {
long currentPosition = 0;
names.clear();
while (currentPosition < raf.length()) {
//读出客户名,存在Pair对象中
Pair<String, String>customer = CustomerDAO.readCustomer(currentPosition, raf);
//对读出的客户名进行拼接操作
String name = clearSpaces(customer.getKey()) + ',' + clearSpaces(customer.getValue());
// System.out.println(name);
names.add(name);//在客户名列表中新增 //自增(偏移量)
//注意要2*CustomerDAO.CUSTOMER_SIZE
//具体原因不清楚,但.dat文件中每条数据的长度为2 * CustomerDAO.CUSTOMER_SIZE
//因此偏移量为2 * CustomerDAO.CUSTOMER_SIZE而不是CustomerDAO.CUSTOMER_SIZE
currentPosition += 2 * CustomerDAO.CUSTOMER_SIZE;
}
//获取姓名
ObservableList<String>items = FXCollections.observableArrayList(names);
customersList.setItems(items);
} //清空后缀空格
public String clearSpaces(String s) {
char[] chars = s.toCharArray();
for (int i = s.length() - 1; i >= 0; i--) {
if (chars[i] != ' ') {
//从后往前找到第一个非空格的字符并截取
return s.substring(0, i + 1);
}
}
return "";//若原字符串全是空格/为空则返回空串
} }

配置启动类

在MainWindow.java中简单修改一下窗口尺寸和title即可运行

package banking.UI;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage; import java.io.IOException; public class MainWindow extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("mainWindow.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 577, 286);
stage.setTitle("banking");
stage.setScene(scene);
stage.show();
} public static void main(String[] args) {
launch();
}
}

运行启动类

运行后效果如图:

JavaFX入门笔记的相关教程结束。

《JavaFX入门笔记.doc》

下载本文的Word格式文档,以方便收藏与打印。