You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFx跨Tab控制器点击保存后刷新另一Tab内TableView的实现方案咨询

JavaFX+SQLite项目实现保存数据后自动刷新TableView解决方案

问题描述

我使用JavaFx结合Sqlite开发了一款演示应用,包含两个Tab页:

  • tab1-Info页内置名为myTable的TableView组件,仅展示id、name、lastname三个字段
  • tab2-Register页提供文本输入框用于录入姓名、姓氏信息
    界面效果如下:
    Info页
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 16:36:02