繁体   English   中英

如何从一个 JavaFX 选项卡添加到数据库以反映在不同的 JavaFX 选项卡中?

[英]How do I get additions to a database made from one JavaFX tab to be reflected in a different JavaFX tab?

我有一个围绕 JavaFXML、scenebuilder 和 Derby 数据库构建的简单的两个选项卡应用程序。 有一个 MainController 类声明为:

package tabpane.view;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;

public class MainController {
  @FXML public TabPane tabPane;
  @FXML public Tab inputTab;
  @FXML public Tab accountTab;
  @FXML private Button exitBtn;

  @FXML private void handleExitBtn() {
    System.exit(0);
  }
}

InputController 类定义了第一个名为 Input 的选项卡。 它整理了一些基本数据,包括一个新的帐户名称和一个复选框,以指示该帐户是否处于活动状态。 收集输入后,它会通过 DAO 控制器类写入 derby 数据库中的表:

package tabpane.view;

import java.net.URL;
import java.sql.SQLException;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TextField;
import tabpane.db.NdDao;
import tabpane.model.AccountObject;

public class InputController implements Initializable
{
  @FXML private TextField accName;
  @FXML private CheckBox isEnabled;
  @FXML private Button saveBtn;  

  @Override
  public void initialize(URL location, ResourceBundle resources) {}

  @FXML private void handleSaveBtn() throws SQLException {
    AccountObject obj = new AccountObject(accName.getText(), isEnabled.isSelected());
    NdDao.connect();
    NdDao.insertAccount(obj);
    NdDao.disconnect();
  }
}

这是 DAO 控制器类中的代码:

package tabpane.db;

import tabpane.model.AccountObject;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.TreeMap;

public class NdDao 
{  
  private static final String strAccountInsert = "INSERT INTO ACCOUNTS " +
            "   (ACCNAME, ISENABLED) " + "VALUES (?, ?)";
  private static final String strAccountSelectAll = "SELECT * from ACCOUNTS ORDER BY ACCNAME ASC";

  private static PreparedStatement pstmtAccountInsert;
  private static PreparedStatement pstmtAccountSelectAll;

  private static Connection conn = null;

  public static void connect() throws SQLException {
    conn = DriverManager.getConnection("jdbc:derby:C:/Users/" + System.getenv("USERNAME") + "/AppData/Local/TabPaneEx/db" + ";create=true");
    prepareStatements();
  } 

  public static void disconnect() throws SQLException {
    if (conn != null) {
      conn.close();
      conn = null;
    }
  }

  private static void prepareStatements() throws SQLException{
    pstmtAccountInsert  = conn.prepareStatement(strAccountInsert);
    pstmtAccountSelectAll  = conn.prepareStatement(strAccountSelectAll);
  }

  public static void insertAccount(AccountObject obj) throws SQLException
  {  
    pstmtAccountInsert.clearParameters();        
    pstmtAccountInsert.setString(1, obj.getAccName());
    pstmtAccountInsert.setBoolean(2, obj.getIsEnabled());
    pstmtAccountInsert.executeUpdate();         
  }

  public static TreeMap<Integer, AccountObject> selectAccounts() throws SQLException        {
    TreeMap<Integer, AccountObject> map = new TreeMap<>();
    ResultSet results;
    AccountObject obj;
    results = pstmtAccountSelectAll.executeQuery();    
    while(results.next()) {
      obj = new AccountObject(
                results.getString(1),
                results.getBoolean(2)
      );
      map.put( (obj.hashCode() ), obj);       
    } 
    return map;
  }

}

DetailController 类定义了第二个选项卡。 在此类中,通过 DAO 从数据库初始化帐户名称的组合框:

package tabpane.view;

import java.net.URL;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.ResourceBundle;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.AnchorPane;
import tabpane.db.NdDao;
import tabpane.model.AccountObject;

public class DetailController implements Initializable
{
  @FXML public ComboBox<String> accNames;

  @Override
  public void initialize(URL location, ResourceBundle resources) {
    try {
      getAccCBoxView();
    } catch (SQLException ex) {
      Logger.getLogger(DetailController.class.getName()).log(Level.SEVERE, null, ex);
    }
    accNames.getSelectionModel().select(0);
  }

  public void getAccCBoxView() throws SQLException
  {
    ObservableList<String> list = FXCollections.observableArrayList();
    NdDao.connect();//
    TreeMap<Integer, AccountObject> map = NdDao.selectAccounts() ;
    NdDao.disconnect();//
    AccountObject obj ;
    Iterator<AccountObject> li = map.values().iterator();
    while (li.hasNext()){
      obj = li.next();
      list.add(obj.getAccName());
    }
    accNames.setItems(list);
  } 
}

首先,在数据库表“L Enqvist”中有一个条目。 在详细信息选项卡组合框中,这看起来像这样: [![图 1][1]][1]

图。1。

然后我通过输入选项卡输入一个新名称并按保存:[![图 2][2]][2]

图2

ACCOUNTS 表的转储显示添加已成功:

ACCNAME ISENABLED
L Enqvist   false
K Berg      false

如果我然后单击“帐户”选项卡,我希望在组合框中找到它:[![图 3][3]][3] 图 3。

但是,组合框没有更新,我看到的只是图 1 中的视图。我真的不知道如何继续,并且非常感谢一些指针,也许是按预期工作所需的代码或一些特定参考信息。 到目前为止,我在互联网上的广泛搜索中一无所获,想知道这种实现是否真的可行。

In first approach I get the following error dump:

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
    at tabpane.view.MainController.reloadAccounts(MainController.java:31)
    at tabpane.view.MainController.lambda$initialize$0(MainController.java:26)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.control.Tab$1.invalidated(Tab.java:209)
    at javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:109)
    at javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:144)
    at javafx.scene.control.Tab.setSelected(Tab.java:185)
    at javafx.scene.control.TabPane$TabPaneSelectionModel.select(TabPane.java:722)
    at javafx.scene.control.TabPane$TabPaneSelectionModel.select(TabPane.java:735)
    at javafx.scene.control.TabPane$TabPaneSelectionModel.select(TabPane.java:656)
    at com.sun.javafx.scene.control.behavior.TabPaneBehavior.selectTab(TabPaneBehavior.java:122)
    at com.sun.javafx.scene.control.skin.TabPaneSkin$TabHeaderSkin$5.handle(TabPaneSkin.java:1332)
    at com.sun.javafx.scene.control.skin.TabPaneSkin$TabHeaderSkin$5.handle(TabPaneSkin.java:1317)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
    at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:394)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$353(GlassViewEventHandler.java:432)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:431)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
    at com.sun.glass.ui.View.notifyMouse(View.java:937)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
    at java.lang.Thread.run(Thread.java:748)

我尝试了许多不同的路径,但没有成功:

detailController = new FXMLLoader(getClass().getResource("DetailView.fxml")).getController();
detailController = new FXMLLoader(getClass().getResource("/DetailView.fxml")).getController();
detailController = new FXMLLoader(getClass().getResource("./DetailView.fxml")).getController();
detailController = new FXMLLoader(getClass().getResource("view/DetailView.fxml")).getController();
detailController = new FXMLLoader(getClass().getResource("/view/DetailView.fxml")).getController();
detailController = new FXMLLoader(getClass().getResource("src/view/DetailView.fxml")).getController();
detailController = new FXMLLoader(getClass().getResource("/src/view/DetailView.fxml")).getController();

我的项目层次结构如下所示:[![图 4][4]][4]

编辑 3 可能的解决方案

  1. 将 Account 组合框从 Detail 窗格移动到 MainController。
  2. 使用 SceneBuilder 将组合框移动到坐标,使其显示为detailPane选项卡的一部分。 同样在 SB 中创建对accNames的引用和对控制它的方法的引用,即handleAccNamesCBox()
  3. 在 MainController 中单击 accountTab 时,将组合框设置为可见。

    公共类 MainController 实现 Initializable { @FXML public TabPane tabPane; @FXML 公共标签 inputTab; @FXML public Tab accountTab; @FXML 公共组合框 accNames;

     private static String selectedAccount; @Override public void initialize(URL location, ResourceBundle resources) { try { accNames.setVisible(false); loadAccounts(); } catch (SQLException ex) { Logger.getLogger(MainController.class.getName()).log(Level.SEVERE, null, ex); } accountTab.setOnSelectionChanged(event -> { try { loadAccounts(); accNames.setVisible(true); } catch (SQLException ex) { Logger.getLogger(MainController.class.getName()).log(Level.SEVERE, null, ex); } }); inputTab.setOnSelectionChanged(event -> { accNames.setVisible(false); }); accNames.getSelectionModel().select(0); } @FXML private void handleAccNamesCBox () { try { selectedAccount = accNames.getSelectionModel().getSelectedItem().toString(); } catch (NullPointerException e) {} } public static String setSelectedAccount() { return selectedAccount; } public void loadAccounts() throws SQLException { ObservableList<String> list = FXCollections.observableArrayList(); NdDao.connect();// TreeMap<Integer, AccountObject> map = NdDao.selectAccounts() ; NdDao.disconnect();// AccountObject obj ; Iterator<AccountObject> li = map.values().iterator(); while (li.hasNext()){ obj = li.next(); list.add(obj.getAccName()); } accNames.setItems(list); } @FXML private void handleExitBtn() { System.exit(0); } }

输入控制器和 inputPane 没有变化,并且从 detailPane 中剥离了大部分处理逻辑:

public class DetailController implements Initializable{

  @FXML private AnchorPane detailPane;

  @Override
  public void initialize(URL location, ResourceBundle resources) {}

}

仍然需要一个解决方案来从 MainController 检索所选值并在 DetailController 中对其进行处理,但至少组合框更新现在按预期工作。 在尝试以某种方式加载选项卡时尝试解决持久性空指针异常的几天后,我现在确信没有其他解决方案,并且 JavaFX 上通过对象属性的自动更新功能无法像我那样工作原本希望。 在同一视图中更新对象时很好,但是当人们尝试从一个视图(如一个选项卡)更新对象并在另一个视图(即不同的选项卡)中获取更新的结果时,它会失败。

解决方案似乎是通过在主控制器中嵌入对选项卡控制器的引用,从主控制器访问相关选项卡的控制器视图,然后通过嵌入式选项卡的 GUI 上的控制器执行您需要的任何操作。 所以主控制器现在看起来像这样:

package tabpane.view;

import java.net.URL;
import java.sql.SQLException;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;

public class MainController implements Initializable{
  @FXML private TabPane tabPane;
  // Inject input tab content.
  @FXML private Tab inputTab;
  // Inject input tab controller
  @FXML private InputController includedInputViewController;
  // Inject account detail tab.
  @FXML private Tab detailTab;
  // Inject account detail tab controller
  @FXML private DetailController includedDetailViewController;
  @FXML private Button exitBtn;

  @Override
  public void initialize(URL location, ResourceBundle resources) {     
    detailTab.setOnSelectionChanged(event -> {
      reloadAccounts();
    });
  }

  private void reloadAccounts() {
    try {
      includedDetailViewController.getAccCBoxView();
    } catch (SQLException ex) {
        ex.printStackTrace();
    }
    includedDetailViewController.accNames.getSelectionModel().select(0);
  }

  @FXML private void handleExitBtn() {
    System.exit(0);
  }
}

从主控制器到 fxml 视图的引用映射可以在主 fxml 中的fx:include语句中看到。 重要的是要注意 fxml 中的嵌入视图 id 必须在主控制器中附加Controller 这是这种嵌入式视图的 fxml 命名约定。

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tabpane.view.MainController">
 <children>
      <TabPane fx:id="tabPane" prefHeight="345.0" prefWidth="600.0" style="-fx-background-color: blue;" tabClosingPolicy="UNAVAILABLE">
        <tabs>
          <Tab fx:id="inputTab" closable="false" text="Input">
            <content> 
               <fx:include fx:id="includedInputView" source="InputView.fxml" />
            </content>
          </Tab>
          <Tab fx:id="detailTab" closable="false" text="Detail">
            <content> 
               <fx:include fx:id="includedDetailView" source="DetailView.fxml" />
            </content>
          </Tab>
        </tabs>
      </TabPane>
      <Button fx:id="exitBtn" layoutX="529.0" layoutY="345.0" mnemonicParsing="false" onAction="#handleExitBtn" text="Exit" />
   </children>

</AnchorPane>

在此应用程序中,当用户单击MainController视图中的 Account 选项卡时,始终会执行组合框的更新视图。 因此,如果通过“输入”选项卡更新数据库,然后单击“帐户”选项卡,则数据库的更新视图始终显示在“帐户”选项卡组合框 accNames 中。 Detail 控制器现在看起来像这样:

package tabpane.view;

import java.net.URL;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.ResourceBundle;
import java.util.TreeMap;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
import tabpane.db.NdDao;
import tabpane.model.AccountObject;

public class DetailController implements Initializable 
{
  @FXML protected ComboBox<String> accNames;

  @Override
  public void initialize(URL location, ResourceBundle resources) {
    try {
      getAccCBoxView();
    } catch (SQLException ex) {}
    accNames.getSelectionModel().select(0);
  }

  public void getAccCBoxView() throws SQLException
  {
    ObservableList<String> list = FXCollections.observableArrayList();
    NdDao.connect();//
    TreeMap<Integer, AccountObject> map = NdDao.selectAccounts() ;
    NdDao.disconnect();//
    AccountObject obj ;
    Iterator<AccountObject> li = map.values().iterator();
    while (li.hasNext()){
      obj = li.next();
      list.add(obj.getAccName());
    }
    accNames.setItems(list);
  } 
}

为了记录,输入视图和控制器看起来和以前一样:

package tabpane.view;

import java.net.URL;
import java.sql.SQLException;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TextField;
import tabpane.db.NdDao;
import tabpane.model.AccountObject;

public class InputController implements Initializable   {
  @FXML private TextField accName;
  @FXML private CheckBox isEnabled; 

  @Override
  public void initialize(URL location, ResourceBundle resources) {}

  @FXML private void handleSaveBtn() throws SQLException {
    AccountObject obj = new AccountObject(accName.getText(), isEnabled.isSelected());
    NdDao.connect();
    NdDao.insertAccount(obj);
    NdDao.disconnect(); 
  }
}

输入视图:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tabpane.view.InputController">
   <children>
      <Label ellipsisString="" graphicTextGap="0.0" layoutX="134.0" layoutY="92.0" text="Account holder:" textFill="MAGENTA" />
      <CheckBox fx:id="isEnabled" ellipsisString="" graphicTextGap="0.0" layoutX="247.0" layoutY="137.0" mnemonicParsing="false" prefHeight="21.0" prefWidth="132.0" text="Enable" textFill="MAGENTA" />
      <TextField fx:id="accName" layoutX="247.0" layoutY="87.0" prefHeight="31.0" prefWidth="245.0" />
      <Button fx:id="saveBtn" layoutX="239.0" layoutY="200.0" mnemonicParsing="false" onAction="#handleSaveBtn" style="-fx-background-color: green;" text="Save" textFill="#eeecec" />
   </children>
</AnchorPane>

如果您发现这有点令人困惑,那么我上面对 FXML 组合文档的评论之一中的参考特别有用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM