JavaFx TableView 实现的基于桌面应用的增删改查
做Web开发很多年,桌面应用太久没碰了。现在市面上有很多桌面应用,PC版的,手机版的,大多是用其他语言写的。作为一名Java语言的忠实粉丝,还是想用 Java 语言来做一个桌面应用。闲话少说,现在就来做个增删改查吧。
本Demo实现功能:
1.TableView列表checkbox框、序号、文本、下拉框内容的展示;
2.在TableView中对文本、下拉框编辑并保存;
3.新增一列到TableView列表中;
4.勾选一列或多列进行删除操作;
一、开发环境说明;
springboot 2.1.4.RELEASE
jdk 1.8
二、pom引入架包;
<!-- springboot整合javafx start -->
<dependency>
<groupId>de.roskenet</groupId>
<artifactId>springboot-javafx-support</artifactId>
<version>2.1.6</version>
</dependency>
<!-- springboot整合javafx end -->
<!-- lombok start -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
<!-- lombok end -->
三、实现代码;
1.一个用户bean;
import lombok.Data;
/**
* 用户Bean
* @author 程就人生
* @Date
*/
@Data
public class User {
public User(String name, String uid, int sex) {
this.name = name;
this.uid = uid;
this.sex = sex;
}
private String name;
private String uid;
private int sex;
}
2.一个性别枚举类;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
/**
* 性别枚举
* @author 程就人生
* @Date
*/
public enum Sex {
MALE(1, "男"),
FEMALE(0, "女");
private int key;
private String value;
Sex(Integer key, String value) {
this.key = key.intValue();
this.value = value;
}
public int key() {
return key;
}
public String getValue() {
return value;
}
/**
* 获取所有的value值
* @return
*/
public static List<String> valueList(){
List<String> values = new ArrayList<String>();
for (Sex testEnum : EnumSet.allOf(Sex.class)) {
values.add(testEnum.value);
}
return values;
}
/**
* 通过key获取value
* @param key
* @return
*/
public static String getValue(Integer key) {
Sex[] sexs = values();
for (Sex sex : sexs) {
if (sex.key() == key) {
return sex.value;
}
}
return null;
}
/**
* 通过value获取key
* @param value
* @return
*/
public static Integer getKey(String value) {
Sex[] sexs = values();
for (Sex sex : sexs) {
if (sex.value.equals(value)) {
return sex.key();
}
}
return null;
}
}
3.核心代码;
import java.util.ArrayList;
import java.util.List;
import org.springframework.util.StringUtils;
import javafx.application.Application;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.ChoiceBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;
import lombok.extern.slf4j.Slf4j;
/**
* 参考官网:https://docs.oracle.com/javafx/2/ui_controls/table-view.htm
* 可编辑tableview示例
* @author 程就人生
* @Date
*/
@Slf4j
@SuppressWarnings("restriction")
public class TableViewSamle extends Application {
// tableview列表
private TableView<User> tableView = new TableView<User>();
// 用户获取
private final ObservableList<User> data =
FXCollections.observableArrayList(
new User("张三","001",1),
new User("李四","002",1),
new User("王五","003",1));
final HBox hb = new HBox();
// 被选中的uid
private List<String> uids = new ArrayList<String>();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("用户信息");
stage.setWidth(450);
stage.setHeight(550);
// 设置为可编辑
tableView.setEditable(true);
// 复选框
TableColumn<User,String> checkboxCol = new TableColumn<User,String>("");
checkboxCol.setMinWidth(50);
// 展示值设置
checkboxCol.setCellValueFactory(new PropertyValueFactory<User, String>("uid"));
// 多选框展示
checkboxCol.setCellFactory(new Callback<TableColumn<User, String>, TableCell<User, String>>() {
@Override
public TableCell<User, String> call(TableColumn<User, String> param) {
TextFieldTableCell<User, String> cell = new TextFieldTableCell<User, String>(){
@Override
public void updateItem(String item, boolean empty) {
// 不为空时再展示
if(!StringUtils.isEmpty(item) && !empty){
CheckBox checkBox = new CheckBox();
HBox hbox = new HBox();
hbox.setAlignment(Pos.CENTER);
hbox.getChildren().add(checkBox);
// 增加监听
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if(newValue){
uids.add(item);
}else{
uids.remove(item);
}
}
});
this.setGraphic(hbox);
}
}
};
return cell;
}
});
// 序号展示
TableColumn<User,String> indexCol = new TableColumn<User,String>("序号");
indexCol.setMinWidth(50);
indexCol.setCellValueFactory(new PropertyValueFactory<User, String>("uid"));
// 设置序号展示
indexCol.setCellFactory(new Callback<TableColumn<User, String>, TableCell<User, String>>() {
@Override
public TableCell<User, String> call(TableColumn<User, String> param) {
TableCell<User, String> cell = new TableCell<User, String>(){
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
this.setText(null);
this.setGraphic(null);
if (!empty) {
int rowIndex = this.getIndex() + 1;
this.setText(String.valueOf(rowIndex));
}
}
};
return cell;
}
});
// 员工号的展示和编辑
TableColumn<User,String> uidCol = new TableColumn<User,String>("员工号");
uidCol.setMinWidth(100);
uidCol.setCellFactory(TextFieldTableCell.forTableColumn());
uidCol.setCellValueFactory(new PropertyValueFactory<User, String>("uid"));
// 对编辑进行监听,必须按回车才能监听到更改事件
uidCol.setOnEditCommit(new EventHandler<CellEditEvent<User, String>>() {
@Override
public void handle(CellEditEvent<User, String> t) {
// 更新改变的值
User temp = ((User) t.getTableView().getItems().get(t.getTablePosition().getRow()));
temp.setUid(t.getNewValue());
// TODO 其他操作
log.info("最新输入的值{}", t.getNewValue());
log.info(temp.toString());
}
});
// 姓名的展示和编辑
TableColumn<User,String> nameCol = new TableColumn<User,String>("姓名");
nameCol.setMinWidth(100);
// 设置为可编辑的,如果是其他类型,可使用 new IntegerStringConverter()等转换
// 参考:https://docs.oracle.com/javase/8/javafx/api/javafx/util/StringConverter.html
nameCol.setCellFactory(TextFieldTableCell.forTableColumn());
nameCol.setCellValueFactory(new PropertyValueFactory<User, String>("name"));
// 对编辑进行监听,必须按回车才能监听到更改事件
nameCol.setOnEditCommit(new EventHandler<CellEditEvent<User, String>>() {
@Override
public void handle(CellEditEvent<User, String> t) {
// 更新改变的值
User temp = ((User) t.getTableView().getItems().get(t.getTablePosition().getRow()));
temp.setName(t.getNewValue());
// TODO 其他操作
log.info("最新输入的值{}", t.getNewValue());
log.info(temp.toString());
}
});
TableColumn<User,String> sexCol = new TableColumn<User,String>("性别");
sexCol.setMinWidth(50);
sexCol.setCellValueFactory(new PropertyValueFactory<User, String>("sex"));
// 性别下拉框的展示
sexCol.setCellValueFactory(new Callback<CellDataFeatures<User, String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<User, String> p) {
// 通过产品名称获取产品型号编码
return new ReadOnlyObjectWrapper<String>(Sex.getValue(p.getValue().getSex()));
}
});
// 性别的获取
sexCol.setCellFactory(ChoiceBoxTableCell.forTableColumn(FXCollections.observableArrayList(Sex.valueList())));
// 对编辑进行监听,必须按回车才能监听到更改事件
sexCol.setOnEditCommit(new EventHandler<CellEditEvent<User, String>>() {
@Override
public void handle(CellEditEvent<User, String> t) {
User temp = ((User) t.getTableView().getItems().get(t.getTablePosition().getRow()));
// 通过value获得key
temp.setSex(Sex.getKey(t.getNewValue()));
}
});
// 数据填充
tableView.setItems(data);
tableView.getColumns().add(checkboxCol);
tableView.getColumns().add(indexCol);
tableView.getColumns().add(uidCol);
tableView.getColumns().add(nameCol);
tableView.getColumns().add(sexCol);
final TextField addUid = new TextField();
addUid.setMaxWidth(uidCol.getPrefWidth());
addUid.setPromptText("员工号");
final TextField addName = new TextField();
addName.setPromptText("姓名");
addName.setMaxWidth(nameCol.getPrefWidth());
// 性别下拉框
final ChoiceBox<String> addSex = new ChoiceBox<String>();
addSex.getItems().addAll(Sex.valueList());
addSex.setMaxWidth(sexCol.getPrefWidth());
// 默认选第一个
addSex.getSelectionModel().select(0);
final Button addButton = new Button("增加");
addButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
data.add(new User(addName.getText(),addUid.getText(),Sex.getKey(addSex.getSelectionModel().getSelectedItem().toString())));
addUid.clear();
addName.clear();
addSex.getSelectionModel().select(0);
// 表格刷新
tableView.refresh();
}
});
final Button delButton = new Button("删除");
delButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
uids.stream().forEach(uid->{
data.remove(data.stream().filter(user->user.getUid().equals(uid)).findFirst().get());
});
// 删除后清空checkbox
uids.clear();
// 表格刷新
tableView.refresh();
}
});
final Label label = new Label("用户列表");
label.setFont(new Font("Arial", 20));
hb.getChildren().addAll(addUid, addName, addSex, addButton);
hb.setSpacing(3);
HBox hb2 = new HBox();
hb2.setSpacing(5);
// 设置padding 上、右、低、左
hb2.setPadding(new Insets(10, 0, 0, 10));
hb2.getChildren().addAll(label,delButton);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(hb2, tableView, hb);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
}
4.运行结果;
问题总结:
1. 在tableview对文本进行编辑的时候,为什么监听事件不生效?
文本输入后,需要按下回车键,监听事件才能生效。
2. choicebox 有value和lable,怎么展示?
看demo,展示用label,存储用value,在setCellValueFactory中设置展示,在setOnEditCommit中监听保存。
3.如果字段不是String类型,在setCellFactory时该如何设置?
除了String类型,还支持很多其他类型,比如:IntegerStringConverter等;
table-view 参考文档:
https://docs.oracle.com/javafx/2/ui_controls/table-view.htm
类型转换参考文档:
https://docs.oracle.com/javase/8/javafx/api/javafx/util/StringConverter.html
lombok使用参考:
Lombok中@Data等注解的使用
最后总结
JavaFx还有很多架包可以使用,包括样式上的封装,控件的再次封装、功能增强等等,但是基本用法都是一样的。当然上面的demo简单了点,样式上也丑了点,但这只是个开始,一切都会越来越好的。