JavaFx跨Tab控制器点击保存后刷新另一Tab内TableView的实现方案咨询
JavaFX+SQLite项目实现保存数据后自动刷新TableView解决方案
问题描述
我使用JavaFx结合Sqlite开发了一款演示应用,包含两个Tab页:
- tab1-Info页内置名为
myTable的TableView组件,仅展示id、name、lastname三个字段 - tab2-Register页提供文本输入框用于录入姓名、姓氏信息
界面效果如下:

项目使用tab1.fxml和tab2.fxml两个布局文件,其中tab1.fxml绑定Controller.java控制器,tab2.fxml绑定Tab2Controller.java控制器。目前该应用基础功能运行正常,但我无法实现点击tab2的「保存」按钮后,自动刷新tab1中myTable组件最新内容的需求,希望点击保存按钮后myTable可自动更新展示最新插入的数据。
项目相关代码
principal.java
import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; public class principal extends Application { public static void main(String[] args) { Application.launch(principal.class, args); } @Override public void start(Stage stage) throws Exception { Parent root = FXMLLoader.load(getClass().getResource("scheme.fxml")); stage.setTitle("Demo tab version"); stage.setScene(new Scene(root, 300, 400)); stage.show(); } }
scheme.fxml
<?xml version="1.0" encoding="UTF-8"?> <?import java.net.*?> <?import javafx.geometry.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <?import javafx.scene.text.*?> <GridPane xmlns:fx="http://javafx.com/fxml" hgap="20" vgap="20" styleClass="root" gridLinesVisible="false"> <padding><Insets top="25" right="2" bottom="20" left="2" /></padding> <Text id="welcome-text" text="Demo tab" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="6" GridPane.rowSpan="1"/> <TabPane fx:id="tabPane" xmlns:fx="http://javafx.com/fxml" prefHeight="500.0" prefWidth="300.0" GridPane.columnIndex="0" GridPane.rowIndex="1" GridPane.columnSpan="6" tabClosingPolicy="UNAVAILABLE"> <tabs> <Tab fx:id="tab1" text="Info" > <fx:include source="Tab1.fxml"/> </Tab> <Tab fx:id="tab2" text="Register" > <fx:include source="Tab2.fxml"/> </Tab> </tabs> </TabPane> <stylesheets> <URL value="@Login.css" /> </stylesheets> </GridPane>
Tab1.fxml
<?xml version="1.0" encoding="UTF-8"?> <?import java.net.*?> <?import javafx.geometry.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <?import javafx.scene.text.*?> <?import javafx.scene.control.TabPane?> <?import javafx.scene.control.Tab?> <?import javafx.scene.control.ComboBox?> <?import javafx.scene.control.cell.*?> <GridPane fx:controller="Controller" xmlns:fx="http://javafx.com/fxml" hgap="20" vgap="20" > <!-- <ScrollPane visible="true" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="1" GridPane.rowSpan="5"> --> <VBox layoutX="15.0" layoutY="76.0" prefHeight="528.0" prefWidth="1200.0"> <children> <TableView fx:id="myTable" prefHeight="512.0" prefWidth="500.0" /> </children> </VBox> </GridPane>
Tab2.fxml
<?xml version="1.0" encoding="UTF-8"?> <?import java.net.*?> <?import javafx.geometry.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <?import javafx.scene.text.*?> <?import javafx.scene.control.TabPane?> <?import javafx.scene.control.Tab?> <?import javafx.scene.control.Label?> <GridPane xmlns:fx="http://javafx.com/fxml" fx:controller="Tab2Controller" hgap="10" vgap="5"> <Text id="informacion" text="Personal Information" GridPane.columnIndex="1" GridPane.rowIndex="0" style="-fx-fill:white" /> <Text text="Name" GridPane.columnIndex="1" GridPane.rowIndex="2" style="-fx-fill:white"/> <TextField fx:id="name" GridPane.columnIndex="1" GridPane.rowIndex="3" prefWidth="150.0"/> <Text text="Lastname" GridPane.columnIndex="1" GridPane.rowIndex="4" style="-fx-fill:white" /> <TextField fx:id="lastname" GridPane.columnIndex="1" GridPane.rowIndex="5" prefWidth="300.0" /> <Button text="Save" GridPane.columnIndex="1" GridPane.rowIndex="7" prefHeight="20" prefWidth="90" GridPane.halignment="RIGHT" onAction="#handleSubmitButtonAction"/> </GridPane>
Controller.java
import java.sql.*; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.layout.VBox; import javafx.stage.Stage; import java.sql.Connection; import java.sql.ResultSet; import java.sql.DriverManager; import java.sql.SQLException; import java.net.URL; import java.util.ResourceBundle; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Label; public class Controller implements Initializable{ @FXML public TableView myTable; @Override public void initialize(URL url, ResourceBundle rb) { TableColumn<Integer, Person> column1 = new TableColumn<>("Id"); column1.setCellValueFactory(new PropertyValueFactory<>("id")); TableColumn<String, Person> column2 = new TableColumn<>("Name"); column2.setCellValueFactory(new PropertyValueFactory<>("nomCom")); TableColumn<String, Person> column3 = new TableColumn<>("LastName"); column3.setCellValueFactory(new PropertyValueFactory<>("ApellPatern")); myTable.getColumns().add(column1); myTable.getColumns().add(column2); myTable.getColumns().add(column3); String sql = "SELECT id, nomCom, ApellPatern FROM warehouses"; try (Connection conn = this.connect(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)){ System.out.println("Opened database successfully"); while (rs.next()) { System.out.println(rs.getInt("id")+ "\t" + rs.getString("nomCom") + "\t" + rs.getString("ApellPatern")); myTable.getItems().add(new Person(rs.getInt("id"),rs.getString("nomCom"),rs.getString("ApellPatern"))); }/*Aqui finaliza el while */ /*Aqui finaliza el executequery*/ } catch (SQLException e) { System.out.println(e.getMessage()); } }/* End of initialize */ public class Person { private Integer id=null; private String nomCom=null; private String ApellPatern=null; public Person(Integer id, String nomCom, String ApellPatern){ this.id=id; this.nomCom=nomCom; this.ApellPatern=ApellPatern; } public Integer getId(){ return id; } public String getNomCom() { return nomCom; } public String getApellPatern(){ return ApellPatern; } } private Connection connect() { String url ="jdbc:sqlite:test.db"; Connection conn = null; try { conn = DriverManager.getConnection(url); } catch (SQLException e) { System.out.println(e.getMessage()); } return conn; } }
Tab2Controller.java
import java.sql.*; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.layout.VBox; import javafx.stage.Stage; import java.sql.Connection; import java.sql.ResultSet; import java.sql.DriverManager; import java.sql.SQLException; import java.net.URL; import java.util.ResourceBundle; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Label; import java.sql.SQLException; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Alert; public class Tab2Controller implements Initializable{ /*@FXML private fx:id="name" */ @FXML private TextField name; /*@FXML private fx:id="lastname" ;*/ @FXML private TextField lastname; @Override public void initialize(URL url, ResourceBundle rb) { /* No information*/ } @FXML protected void handleSubmitButtonAction(ActionEvent event) { String nomCom=name.getText(); String ApellPatern=lastname.getText(); String sql = "INSERT INTO warehouses(nomCom,ApellPatern) VALUES(?,?)"; try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1,nomCom); pstmt.setString(2, ApellPatern); pstmt.executeUpdate(); } catch (SQLException e) { System.out.println(e.getMessage()); } Alert alert = new Alert(AlertType.INFORMATION); alert.setTitle("Information Dialog"); alert.setHeaderText(null); alert.setContentText("The information has been saved succesfully"); alert.showAndWait(); } private Connection connect() { // SQLite connection string // String url = "jdbc:sqlite:C://sqlite/db/test.db"; String url ="jdbc:sqlite:test.db"; Connection conn = null; try { conn = DriverManager.getConnection(url); } catch (SQLException e) { System.out.println(e.getMessage()); } return conn; } }
实现方案
我们通过父控制器作为中间层关联两个子控制器+封装表格刷新逻辑的方式实现需求,总共需要4处修改:
1. 给Controller.java新增公共刷新方法
把加载表格数据的逻辑抽成独立方法,方便外部调用刷新:
// Controller.java新增方法 public void refreshTable() { // 清空原有数据 myTable.getItems().clear(); String sql = "SELECT id, nomCom, ApellPatern FROM warehouses"; try (Connection conn = this.connect(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)){ while (rs.next()) { myTable.getItems().add(new Person(rs.getInt("id"),rs.getString("nomCom"),rs.getString("ApellPatern"))); } } catch (SQLException e) { System.out.println(e.getMessage()); } }
修改原initialize方法直接调用该方法即可:
@Override public void initialize(URL url, ResourceBundle rb) { // 原有列初始化逻辑保留 TableColumn<Integer, Person> column1 = new TableColumn<>("Id"); column1.setCellValueFactory(new PropertyValueFactory<>("id")); TableColumn<String, Person> column2 = new TableColumn<>("Name"); column2.setCellValueFactory(new PropertyValueFactory<>("nomCom")); TableColumn<String, Person> column3 = new TableColumn<>("LastName"); column3.setCellValueFactory(new PropertyValueFactory<>("ApellPatern")); myTable.getColumns().addAll(column1, column2, column3); // 调用抽离的刷新方法 refreshTable(); }
2. 给Tab2Controller新增接收Tab1控制器的属性
// Tab2Controller.java新增属性和set方法 private Controller tab1Controller; public void setTab1Controller(Controller tab1Controller) { this.tab1Controller = tab1Controller; }
3. 新增scheme.fxml对应的父控制器
新建SchemeController.java作为scheme布局的控制器,用于关联两个子控制器:
import javafx.fxml.FXML; import javafx.fxml.Initializable; import java.net.URL; import java.util.ResourceBundle; public class SchemeController implements Initializable { // 属性名必须和<fx:include>的fx:id加Controller后缀一致,JavaFX会自动注入 @FXML private Controller tab1Controller; @FXML private Tab2Controller tab2Controller; @Override public void initialize(URL url, ResourceBundle rb) { // 将tab1的控制器实例传递给tab2 tab2Controller.setTab1Controller(tab1Controller); } }
修改scheme.fxml绑定该控制器,给<fx:include>加上fx:id:
<!-- GridPane标签新增控制器绑定 --> <GridPane xmlns:fx="http://javafx.com/fxml" fx:controller="SchemeController" hgap="20" vgap="20" styleClass="root" gridLinesVisible="false"> <!-- 两个<fx:include>新增fx:id --> <fx:include fx:id="tab1" source="Tab1.fxml"/> <fx:include fx:id="tab2" source="Tab2.fxml"/>
4. 保存成功后触发刷新
修改Tab2Controller的保存逻辑,插入数据成功后调用刷新方法:
@FXML protected void handleSubmitButtonAction(ActionEvent event) { String nomCom=name.getText(); String ApellPatern=lastname.getText(); String sql = "INSERT INTO warehouses(nomCom,ApellPatern) VALUES(?,?)"; try (Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1,nomCom); pstmt.setString(2, ApellPatern); pstmt.executeUpdate(); // 插入成功后刷新tab1表格 tab1Controller.refreshTable(); } catch (SQLException e) { System.out.println(e.getMessage()); } Alert alert = new Alert(AlertType.INFORMATION); alert.setTitle("Information Dialog"); alert.setHeaderText(null); alert.setContentText("The information has been saved succesfully"); alert.showAndWait(); // 可选:清空输入框方便下次录入 name.clear(); lastname.clear(); }
实现原理
JavaFX中通过<fx:include>引入的子布局,对应的控制器会自动注入到父控制器中[子布局fx:id]Controller命名的属性里,我们通过父控制器作为中间层传递tab1的控制器实例给tab2,就能在tab2保存数据后主动调用tab1的刷新方法,实现表格自动更新。
内容的提问来源于stack exchange,提问作者Omar Elio Torres Castillo
相关产品推荐
相关产品推荐

