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

JavaFX页面切换时如何保留数组与对象中的已保存数据?

问题原因
  1. 控制器实例重复创建:每次切换页面后返回,FXMLLoader会重新加载FXML文件并创建新的ListProdukScreen控制器对象。之前添加的数据(如datalist、daftarKategori)属于旧控制器实例,新实例的所有数据会回到初始状态,导致数据看起来被重置。
  2. 数据存储在控制器内部:商品列表和分类数据都是ListProdukScreen的成员变量,每个控制器实例都有独立的副本,没有全局共享的数据源。
解决方案

方案一:全局单例数据源(推荐小型应用使用)

创建全局数据管理类,用单例模式确保整个应用只有一份数据源,所有页面共享这套数据。

1. 创建DataManager类

package com.finalalp;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class DataManager {
    // 全局唯一实例
    private static final DataManager INSTANCE = new DataManager();
    
    // 共享的商品列表
    private ObservableList<Produk> produkList = FXCollections.observableArrayList();
    // 共享的分类数据
    private Kategori daftarKategori = new Kategori(null);
    
    // 私有构造,禁止外部实例化
    private DataManager() {
        // 初始化默认分类
        daftarKategori.makanan = new String[]{
            "1. Nama kategori", 
            "2. Nama kategori", 
            "3. Nama kategori", 
            "4. Nama kategori", 
            "5. Nama kategori"
        };
    }
    
    // 获取单例实例
    public static DataManager getInstance() {
        return INSTANCE;
    }
    
    // 获取商品列表
    public ObservableList<Produk> getProdukList() {
        return produkList;
    }
    
    // 获取分类数据
    public Kategori getDaftarKategori() {
        return daftarKategori;
    }
}

2. 修改ListProdukScreen控制器

package com.finalalp;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.beans.Observable;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;

public class ListProdukScreen implements Initializable {
    // 从全局数据管理器获取数据源,不再自行创建
    private Kategori daftarKategori = DataManager.getInstance().getDaftarKategori();
    private ObservableList<Produk> datalist = DataManager.getInstance().getProdukList();

    @FXML
    void switchToHomeScreen(ActionEvent event) throws IOException {
        App.setRoot("homeScreen");
    }

    @FXML
    private ChoiceBox<String> addListKategori;
    @FXML
    private ChoiceBox<String> listKategori;
    @FXML
    private ChoiceBox<String> listPilihKategori;
    @FXML
    private TableView<Produk> tableListProduk;
    @FXML
    private TableColumn<Produk, String> deskripsi;
    @FXML
    private TableColumn<Produk, Number> harga;
    @FXML
    private TableColumn<Produk, String> nama;
    @FXML
    private TableColumn<Produk, String> kategoriTable;
    @FXML
    private TextField namaKategoriBaru;
    @FXML
    private TextField namaProduk;
    @FXML
    private TextField deskripsiProduk;
    @FXML
    private TextField hargaProduk;
    @FXML
    private TextField searchBarProduk;

    @FXML
    void tambahkanProduk(ActionEvent event) {
        // 构造方法已完成赋值,无需重复调用set
        Produk produkAdded = new Produk(
            namaProduk.getText(),
            deskripsiProduk.getText(),
            listPilihKategori.getValue(),
            Integer.parseInt(hargaProduk.getText())
        );
        datalist.add(produkAdded);
        // 表格已绑定到过滤后的列表,无需手动添加
        namaProduk.clear();
        deskripsiProduk.clear();
        hargaProduk.clear();
    }

    @FXML
    void pilihListKategori(MouseEvent event) {
        searchBarProduk.setText(listKategori.getValue());
    }

    @FXML
    void filterPencarian(MouseEvent event) {
        FilteredList<Produk> filteredListdata = new FilteredList<>(datalist, b -> true);
        searchBarProduk.textProperty().addListener((Observable, oldValue, newValue) -> {
            filteredListdata.setPredicate(produk -> {
                if (newValue == null || newValue.isBlank()) {
                    return true;
                }
                String lowerCaseFilter = newValue.toLowerCase();
                return produk.getKategori().toLowerCase().contains(lowerCaseFilter)
                        || produk.getNama().toLowerCase().contains(lowerCaseFilter);
            });
        });

        SortedList<Produk> sortedData = new SortedList<>(filteredListdata);
        sortedData.comparatorProperty().bind(tableListProduk.comparatorProperty());
        tableListProduk.setItems(sortedData);
    }

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        // 初始化所有下拉框的分类数据
        addListKategori.getItems().setAll(daftarKategori.makanan);
        listKategori.getItems().setAll(daftarKategori.makanan);
        listPilihKategori.getItems().setAll(daftarKategori.makanan);

        // 表格列绑定
        nama.setCellValueFactory(new PropertyValueFactory<>("nama"));
        harga.setCellValueFactory(new PropertyValueFactory<>("harga"));
        deskripsi.setCellValueFactory(new PropertyValueFactory<>("deskripsi"));
        kategoriTable.setCellValueFactory(new PropertyValueFactory<>("kategori"));

        // 初始化表格数据
        filterPencarian(null);
    }

    public void submitKategoriBaru(ActionEvent event) {
        String namaKategoString = addListKategori.getValue();
        switch (namaKategoString) {
            case "1. Nama kategori":
                daftarKategori.makanan[0] = "1. " + namaKategoriBaru.getText();
                break;
            case "2. Nama kategori":
                daftarKategori.makanan[1] = "2. " + namaKategoriBaru.getText();
                break;
            case "3. Nama kategori":
                daftarKategori.makanan[2] = "3. " + namaKategoriBaru.getText();
                break;
            case "4. Nama kategori":
                daftarKategori.makanan[3] = "4. " + namaKategoriBaru.getText();
                break;
            case "5. Nama kategori":
                daftarKategori.makanan[4] = "5. " + namaKategoriBaru.getText();
                break;
            default:
                break;
        }
        // 更新所有下拉框的分类内容
        addListKategori.getItems().setAll(daftarKategori.makanan);
        listKategori.getItems().setAll(daftarKategori.makanan);
        listPilihKategori.getItems().setAll(daftarKategori.makanan);

        namaKategoriBaru.clear();
    }
}

方案二:复用控制器实例

如果不想用单例,可以在App类中缓存已加载的FXML和控制器实例,切换页面时直接复用,避免重复创建:

// 修改App类的setRoot方法
private static Map<String, Parent> cachedRoots = new HashMap<>();

public static void setRoot(String fxml) throws IOException {
    if (cachedRoots.containsKey(fxml)) {
        root = cachedRoots.get(fxml);
    } else {
        FXMLLoader loader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
        root = loader.load();
        cachedRoots.put(fxml, root);
    }
    scene.setRoot(root);
}

注意:这种方式适合页面较少的场景,若页面依赖外部状态变化,需要额外处理数据同步。

方案三:持久化数据(可选)

如果需要关闭应用后仍保留数据,可以将数据序列化到文件(如JSON、XML)或存入数据库:

  • 用Jackson库将ObservableList<Produk>和Kategori序列化为JSON文件
  • 在应用启动时读取文件加载数据,数据变更时写入文件

内容的提问来源于stack exchange,提问作者Alizaenal Abidin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 20:01:21