JavaFX TableView 自定义可编辑状态的切换和监听
网上的可查到的关于Java FX的表格内的编辑多是以在设置setEditable(true)后,再通过添加各种方法进行可编辑操作和编辑后的提交。但这些均存在一个操作逻辑的问题,均需要双击行列才能进入切换TextField进行编辑,因此在UI界面中若没有添加提示内容,用户将无法直观感知进入编辑操作的方法,会大大提高了用户的使用成本。因此从交互的角度,想到以各种方案
问题描述
最近在写图书馆系统用到了大量表格,也需要对表格内容进行直接编辑,但是经过搜索得到的JavaFX的表格内的编辑是以在设置setEditable(true)
后,再通过添加各种方法进行可编辑操作和编辑后的提交。这种存在一个操作逻辑的问题,需要双击行列才能进入切换TextField进行编辑,因此在UI界面中若没有添加提示内容,用户将无法直观感知进入编辑操作的方法,会大大提高了用户的使用成本。因此从交互的角度,想到了各种方案,通过按钮点击、直接显示、点击表格自动切换的思路,实现易于用户使用的对表格操作的UI交互。
PS: 后想到部分博客的内容也用到自定义表格方案,但思路并不清晰,甚至有毫无解释的直接抛代码的,所以没啥心情去认真看。
思路分析
不能通过setCellFactory(TextFieldTableCell.forTableColumn())
这样最简单的内置方法实现编辑,需要自己的自定义实现,虽略显麻烦,但十分好用。
自定义表格样式思路:
- JavaFX中的TableView均是对Column进行操作,因此修改会直接修改整列的样式
- 若不想将整列变化,可以通过定位聚焦当行的位置,行列的交点则为修改样式的单表格
- 自定义样式内最好使用布局(HBox, VBox)填充,一是方便其随表格的变化而变化,二是更好设置所需的内容
- 此次演示均以TextField实现,包含但不限于
CheckBox / ChoiceBox / ComboBox / ProgressBar
- 此次演示数据类型均为String,其他类型需自行调整
交互逻辑思路
- 直接显示TextField(不建议)
- 通过点击按钮在默认与TextField状态切换
- 通过直接点击表格显示TextField
思路实现
直接显示TextField(基础)
column.setCellFactory(new Callback<TableColumn<BookManageRecord, String>, TableCell<BookManageRecord, String>>() {
//column为更改样式的目标列名
@Override
public TableCell<BookManageRecord, String> call(TableColumn<BookManageRecord, String> param) {
//设置工厂方法并覆盖实现Callback其中的TableCell定义
TableCell<BookManageRecord, String> cell = new TableCell<BookManageRecord, String>(){
//通过覆盖其TableCell的定义实现自定义样式
@Override
protected void updateItem(String item, boolean empty) {
//覆盖TableCell的样式更新
super.updateItem(item, empty);
if(!empty && item != null){
//核心内容
HBox hbox = new HBox(); //通过HBox布局填充表格便于管理
hbox.setAlignment(Pos.CENTER); //内容居中
TextField tf = new TextField(item); //用获取到的item初始化TextField
hbox.getChildren().add(tf); //将TextField加入HBox布局
this.setGraphic(hbox); //将表格样式设置为HBox
}
}
};
return cell; //返回该表格样式与内容
}
)};
该方法缺点在于直接显示TextField无法切换回初始状态,但依旧可以实现当焦点不在该Textfield时提交内容,视情况而定吧!
通过Button点击切换
private int flag = 1;
button.setOnAction(new EventHandler<ActionEvent>() {
//button添加监听
@Override
public void handle(ActionEvent event) {
if(flag == 1){
setToTextField();
flag--;
}
else{
setToLabel();
flag++;
}
}
});
private void setToTextField(){
cat.setCellFactory(new Callback<TableColumn<BookManageRecord, String>, TableCell<BookManageRecord, String>>() {
@Override
public TableCell<BookManageRecord, String> call(TableColumn<BookManageRecord, String> param) {
TableCell<BookManageRecord, String> cell = new TableCell<BookManageRecord, String>(){
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if(!empty && item != null){
TextField tf = new TextField(item);
HBox hbox = new HBox();
hbox.setAlignment(Pos.CENTER);
hbox.getChildren().add(tf);
this.setGraphic(hbox);
}
}
};
return cell;
}
});
}
private void setToLabel(){
cat.setCellFactory(new Callback<TableColumn<BookManageRecord, String>, TableCell<BookManageRecord, String>>() {
@Override
public TableCell<BookManageRecord, String> call(TableColumn<BookManageRecord, String> param) {
TableCell<BookManageRecord, String> cell = new TableCell<BookManageRecord, String>(){
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if(!empty && item != null){
Label label = new Label(item); //此处唯一不同便是使用了Label
//这样一来和默认的样式区别不大
HBox hbox = new HBox();
hbox.setAlignment(Pos.CENTER);
hbox.getChildren().add(label);
this.setGraphic(hbox);
}
}
};
return cell;
}
});
}
updateItem()
是在一列表格中,依次从上到下对每一个表格进行一次update操作,因此只要在setCellFactory()
工厂方法中去进行判断全局的状态是该TextField还是Label的话,都会出现TextField和Label间隔出现的问题。
由于是自定义样式,因此在切换回Label的时候是无法监听到TextField内部的修改的,因此需要自定义添加监听,此处只是样式修改,不过多追加描述。
通过点击表格进行修改
table.setRowFactory( tv -> {
TableRow<BookManageRecord> row = new TableRow<BookManageRecord>();
row.setOnMouseClicked(event -> {
//通过对单行的监听,再加单列样式的修改,实现点击坐标位的样式切换
if (event.getClickCount() == 1 && (! row.isEmpty()) ) {
cat.setCellFactory(new Callback<TableColumn<BookManageRecord, String>, TableCell<BookManageRecord, String>>() {
@Override
public TableCell<BookManageRecord, String> call(TableColumn<BookManageRecord, String> param) {
TableCell<BookManageRecord, String> cell = new TableCell<BookManageRecord, String>(){
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if(!empty && item != null){
HBox hbox = new HBox();
hbox.setAlignment(Pos.CENTER);
TextField tf = new TextField(item);
Label label = new Label(item);
if(this.getTableRow().getIndex() == table.getSelectionModel().getSelectedIndex()){
hbox.getChildren().add(tf);
this.setGraphic(hbox);
}
else {
hbox.getChildren().add(label);
this.setGraphic(hbox);
}
}
}
};
return cell;
}
});
}
});
return row ;
});
用类似方法一样能实现用button控制的单表格样式切换,只要确定选中的行Index与需要刷新的行Index是否一致就可以实现了。
方法延伸
该方法下,通过判断item内容,可以实现单列表格内不同内容的不同样式。
参考借鉴
- Bilibili:JavaFX视频教程第98课,TableView,自定义单元格TableCell
- CSDN:使用javafx tableview控件时彻底解决单元格可编辑且可监听
——虽然没有认真看,但是写完回头发现这个的方案不错且适用于ComboBox的。