如何监听 TableCell 中动态生成的按钮的点击?
How to listen for clicks on dynamically generated buttons in TableCell?
我有一个 class 任务,它从数据库中获取字段值并在 table 中显示为一行。
package com.example.javafxapp.models;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
public class Task {
private String id;
private String task;
private Button done;
private Button cancel;
private Button remove;
private HBox edit;
public Task(String id, String task, HBox edit, Button done, Button cancel, Button remove) {
this.id = id;
this.task = task;
this.edit = edit;
this.done = done;
this.cancel = cancel;
this.remove = remove;
edit.getChildren().addAll(done, cancel, remove);
edit.setSpacing(5);
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTask() {
return task;
}
public void setTask(String task) {
this.task = task;
}
public HBox getEdit() {
return edit;
}
public void setEdit(HBox edit) {
this.edit = edit;
}
public Button getDone() {
return done;
}
public void setDone(Button done) {
this.done = done;
}
public Button getCancel() {
return cancel;
}
public void setCancel(Button cancel) {
this.cancel = cancel;
}
public Button getRemove() {
return remove;
}
public void setRemove(Button remove) {
this.remove = remove;
}
}
有一个控制器用任务 class 的对象填充 table 的行。
package com.example.javafxapp.controllers;
import com.example.javafxapp.models.DatabaseHandler;
import com.example.javafxapp.models.Task;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Optional;
import java.util.ResourceBundle;
public class AppController {
@FXML
private ResourceBundle resources;
@FXML
private URL location;
@FXML
private AnchorPane appPane;
@FXML
private TableView<Task> currentTable;
@FXML
private Tab currentTab;
@FXML
private AnchorPane currentTabAnchorPane;
@FXML
private TableColumn<Task, String> currentIdColumn;
@FXML
private TableColumn<Task, String> currentTasksColumn;
@FXML
private TableColumn<Task, HBox> editColumn;
ObservableList<Task> currentTasks = FXCollections.observableArrayList();
@FXML
void initialize() {
getAllTasks();
initCols();
//Вutton mouse click listener from Tableview cells.
}
private void initCols(){
currentIdColumn.setCellValueFactory(new PropertyValueFactory<Task, String>("id"));
currentTasksColumn.setCellValueFactory(new PropertyValueFactory<Task, String>("task"));
editColumn.setCellValueFactory(new PropertyValueFactory<Task, HBox>("edit"));
currentTable.getItems().clear();
currentTable.setItems(currentTasks);
}
private void getAllTasks() {
ResultSet rs = new DatabaseHandler().getTaskTableFromDb();
try {
while (rs.next()) {
if(rs.getString("state").equals("current")) {
currentTasks.add(new Task(rs.getString("idtask"),
rs.getString("task"),
rs.getString("state"),
new HBox(),
new Button("done"),
new Button("cancel"),
new Button("remove")));
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
我需要监听单元格中按钮的点击。
但是这些按钮不会作为控制器中的字段出现。如何在不引用特定单元格的情况下执行此操作?
您的模型 class Task
不应包含任何 Ui 组件。它应该看起来像
public class Task {
private String id;
private String task;
public Task(String id, String task) {
this.id = id;
this.task = task;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTask() {
return task;
}
public void setTask(String task) {
this.task = task;
}
}
您应该强烈考虑使用 JavaFX Properties 实现模型 class,而不是将其作为普通 Java bean 实现。
要在 table 的单元格中显示 UI 控件,您应该实现自定义 TableCell
并在单元格工厂中生成它的实例。可能最方便的方法是让包含这些单元格的列将整个模型作为其值。这样,单元格的 getItem()
方法将 return 其特定行的模型。
@FXML
private TableColumn<Task, Task> editColumn ;
@FXML
private void initialize() {
// ...
editColumn.setCellValueFactory(cellData -> new SimpleObjectProperty<>(cellData.getValue()));
editColumn.setCellFactory(col -> new TaskActionCell());
// ...
}
单元实现(这里是控制器中的内部 class)看起来像
public class TaskActionCell extends TableCell<Task, Task> {
private final HBox graphic;
public TaskActionCell() {
Button done = new Button("Done");
Button cancel = new Button("Cancel");
Button remove = new Button("Remove");
graphic = new HBox(5, done, cancel remove);
remove.setOnAction(event -> {
Task task = getItem();
currentTasks.remove(task);
});
// other event handlers are similar
}
@Override
protected void updateItem(Task task, boolean empty) {
super.updateItem(task, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(graphic);
}
}
}
另一种方法是只让单元格值为空(例如使用 TableColumn<Task, Void> editColumn
等),并使用 getTableView().getItems().get(getIndex())
访问单元格中的行值,但这似乎更少优雅。
这是一个完整的工作示例,使用我在上面定义的 Task
class。
Table.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TableColumn?>
<BorderPane xmlns:fx="http://javafx.com/fxml"
fx:controller="org.jamesd.examples.actiontable.TableController">
<center>
<TableView fx:id="table">
<columns>
<TableColumn text="Id" fx:id="idColumn"/>
<TableColumn text="Task" fx:id="taskColumn"/>
<TableColumn text="Edit" fx:id="editColumn"/>
</columns>
</TableView>
</center>
</BorderPane>
TableController.java
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import java.util.ArrayList;
import java.util.List;
public class TableController {
@FXML
private TableView<Task> table ;
@FXML
private TableColumn<Task, Task> editColumn ;
@FXML
private TableColumn<Task, String> idColumn ;
@FXML
private TableColumn<Task, String> taskColumn ;
private ObservableList<Task> currentTasks ;
@FXML
private void initialize() {
idColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getId()));
taskColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getTask()));
editColumn.setCellValueFactory(cellData -> new SimpleObjectProperty<>(cellData.getValue()));
editColumn.setCellFactory(col -> new TaskActionCell());
currentTasks = loadTasks();
table.setItems(currentTasks);
}
private ObservableList<Task> loadTasks() {
List<Task> tasks = new ArrayList<>();
for (int i = 1 ; i <= 20; i++) {
tasks.add(new Task(Integer.toString(i), "Task "+i));
}
return FXCollections.observableList(tasks);
}
public class TaskActionCell extends TableCell<Task, Task> {
private final HBox graphic;
public TaskActionCell() {
Button done = new Button("Done");
Button cancel = new Button("Cancel");
Button remove = new Button("Remove");
graphic = new HBox(5, done, cancel, remove);
remove.setOnAction(event -> {
Task task = getItem();
currentTasks.remove(task);
});
// other event handlers are similar
}
@Override
protected void updateItem(Task task, boolean empty) {
super.updateItem(task, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(graphic);
}
}
}
}
申请class:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class App extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource("Table.fxml"));
Scene scene = new Scene(fxmlLoader.load());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
申请运行:
按了很多次“删除”按钮后:
我有一个 class 任务,它从数据库中获取字段值并在 table 中显示为一行。
package com.example.javafxapp.models;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
public class Task {
private String id;
private String task;
private Button done;
private Button cancel;
private Button remove;
private HBox edit;
public Task(String id, String task, HBox edit, Button done, Button cancel, Button remove) {
this.id = id;
this.task = task;
this.edit = edit;
this.done = done;
this.cancel = cancel;
this.remove = remove;
edit.getChildren().addAll(done, cancel, remove);
edit.setSpacing(5);
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTask() {
return task;
}
public void setTask(String task) {
this.task = task;
}
public HBox getEdit() {
return edit;
}
public void setEdit(HBox edit) {
this.edit = edit;
}
public Button getDone() {
return done;
}
public void setDone(Button done) {
this.done = done;
}
public Button getCancel() {
return cancel;
}
public void setCancel(Button cancel) {
this.cancel = cancel;
}
public Button getRemove() {
return remove;
}
public void setRemove(Button remove) {
this.remove = remove;
}
}
有一个控制器用任务 class 的对象填充 table 的行。
package com.example.javafxapp.controllers;
import com.example.javafxapp.models.DatabaseHandler;
import com.example.javafxapp.models.Task;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Optional;
import java.util.ResourceBundle;
public class AppController {
@FXML
private ResourceBundle resources;
@FXML
private URL location;
@FXML
private AnchorPane appPane;
@FXML
private TableView<Task> currentTable;
@FXML
private Tab currentTab;
@FXML
private AnchorPane currentTabAnchorPane;
@FXML
private TableColumn<Task, String> currentIdColumn;
@FXML
private TableColumn<Task, String> currentTasksColumn;
@FXML
private TableColumn<Task, HBox> editColumn;
ObservableList<Task> currentTasks = FXCollections.observableArrayList();
@FXML
void initialize() {
getAllTasks();
initCols();
//Вutton mouse click listener from Tableview cells.
}
private void initCols(){
currentIdColumn.setCellValueFactory(new PropertyValueFactory<Task, String>("id"));
currentTasksColumn.setCellValueFactory(new PropertyValueFactory<Task, String>("task"));
editColumn.setCellValueFactory(new PropertyValueFactory<Task, HBox>("edit"));
currentTable.getItems().clear();
currentTable.setItems(currentTasks);
}
private void getAllTasks() {
ResultSet rs = new DatabaseHandler().getTaskTableFromDb();
try {
while (rs.next()) {
if(rs.getString("state").equals("current")) {
currentTasks.add(new Task(rs.getString("idtask"),
rs.getString("task"),
rs.getString("state"),
new HBox(),
new Button("done"),
new Button("cancel"),
new Button("remove")));
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
我需要监听单元格中按钮的点击。 但是这些按钮不会作为控制器中的字段出现。如何在不引用特定单元格的情况下执行此操作?
您的模型 class Task
不应包含任何 Ui 组件。它应该看起来像
public class Task {
private String id;
private String task;
public Task(String id, String task) {
this.id = id;
this.task = task;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTask() {
return task;
}
public void setTask(String task) {
this.task = task;
}
}
您应该强烈考虑使用 JavaFX Properties 实现模型 class,而不是将其作为普通 Java bean 实现。
要在 table 的单元格中显示 UI 控件,您应该实现自定义 TableCell
并在单元格工厂中生成它的实例。可能最方便的方法是让包含这些单元格的列将整个模型作为其值。这样,单元格的 getItem()
方法将 return 其特定行的模型。
@FXML
private TableColumn<Task, Task> editColumn ;
@FXML
private void initialize() {
// ...
editColumn.setCellValueFactory(cellData -> new SimpleObjectProperty<>(cellData.getValue()));
editColumn.setCellFactory(col -> new TaskActionCell());
// ...
}
单元实现(这里是控制器中的内部 class)看起来像
public class TaskActionCell extends TableCell<Task, Task> {
private final HBox graphic;
public TaskActionCell() {
Button done = new Button("Done");
Button cancel = new Button("Cancel");
Button remove = new Button("Remove");
graphic = new HBox(5, done, cancel remove);
remove.setOnAction(event -> {
Task task = getItem();
currentTasks.remove(task);
});
// other event handlers are similar
}
@Override
protected void updateItem(Task task, boolean empty) {
super.updateItem(task, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(graphic);
}
}
}
另一种方法是只让单元格值为空(例如使用 TableColumn<Task, Void> editColumn
等),并使用 getTableView().getItems().get(getIndex())
访问单元格中的行值,但这似乎更少优雅。
这是一个完整的工作示例,使用我在上面定义的 Task
class。
Table.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TableColumn?>
<BorderPane xmlns:fx="http://javafx.com/fxml"
fx:controller="org.jamesd.examples.actiontable.TableController">
<center>
<TableView fx:id="table">
<columns>
<TableColumn text="Id" fx:id="idColumn"/>
<TableColumn text="Task" fx:id="taskColumn"/>
<TableColumn text="Edit" fx:id="editColumn"/>
</columns>
</TableView>
</center>
</BorderPane>
TableController.java
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import java.util.ArrayList;
import java.util.List;
public class TableController {
@FXML
private TableView<Task> table ;
@FXML
private TableColumn<Task, Task> editColumn ;
@FXML
private TableColumn<Task, String> idColumn ;
@FXML
private TableColumn<Task, String> taskColumn ;
private ObservableList<Task> currentTasks ;
@FXML
private void initialize() {
idColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getId()));
taskColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getTask()));
editColumn.setCellValueFactory(cellData -> new SimpleObjectProperty<>(cellData.getValue()));
editColumn.setCellFactory(col -> new TaskActionCell());
currentTasks = loadTasks();
table.setItems(currentTasks);
}
private ObservableList<Task> loadTasks() {
List<Task> tasks = new ArrayList<>();
for (int i = 1 ; i <= 20; i++) {
tasks.add(new Task(Integer.toString(i), "Task "+i));
}
return FXCollections.observableList(tasks);
}
public class TaskActionCell extends TableCell<Task, Task> {
private final HBox graphic;
public TaskActionCell() {
Button done = new Button("Done");
Button cancel = new Button("Cancel");
Button remove = new Button("Remove");
graphic = new HBox(5, done, cancel, remove);
remove.setOnAction(event -> {
Task task = getItem();
currentTasks.remove(task);
});
// other event handlers are similar
}
@Override
protected void updateItem(Task task, boolean empty) {
super.updateItem(task, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(graphic);
}
}
}
}
申请class:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class App extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource("Table.fxml"));
Scene scene = new Scene(fxmlLoader.load());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
申请运行:
按了很多次“删除”按钮后: