当前位置: 首页 > 知识库问答 >
问题:

如何将从一个JavaFX选项卡添加到数据库中的内容反映到另一个JavaFX选项卡中?

巴洲
2023-03-14

我有一个简单的基于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”中有一个条目。在“详细信息”选项卡组合框中,如下所示:[![Fig 1][1][1]

图1。

然后,我通过输入选项卡输入一个新名称,然后按Save:[![Fig 2][2]][2]

图2

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

ACCNAME ISENABLED
L Enqvist   false
K Berg      false

如果我单击Account选项卡,我会在组合框中找到它:[![Fig 3][3][3]Fig 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();

我的项目继承权如上图所示:[![Fig 4][4][4]

编辑3可能的解决方案

>

public class MainController实现可初始化的{@FXML public TabPane TabPane;@FXML public Tab inputTab;@FXML public Tab accountTab;@FXML public ComboBox 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);
    }
}

input controller和inputPane保持不变,大部分处理逻辑从detailPane中剥离:

public class DetailController implements Initializable{

  @FXML private AnchorPane detailPane;

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

}

仍然需要一个解决方案来从MainController中检索所选的值,并在DetailController中对其进行处理,但至少组合框更新现在正在按预期工作。在以某种方式加载选项卡时,我花了几天的时间试图解决持久的空指针异常,现在我确信没有其他解决方案,JavaFX上通过对象属性的自动更新功能并没有按照我最初希望的方式工作。当更新同一视图中的对象时,这很好,但当一个人试图从一个视图(如一个选项卡)更新对象,并在另一个视图(即不同的选项卡)中获得更新结果时,它会崩溃。

共有1个答案

狄峰
2023-03-14

解决方案似乎是从主控制器访问相关选项卡的控制器视图,方法是在主控制器中嵌入对选项卡控制器的引用,然后通过嵌入式选项卡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选项卡时,组合框的更新视图始终会执行。因此,如果用户通过“输入”选项卡更新数据库,然后单击“帐户”选项卡,则数据库的更新视图始终显示在“帐户”选项卡组合框中。细节控制器现在看起来像这样:

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组合文档尤其有用。

 类似资料:
  • 当我像下面的例子一样点击选项卡2时,我如何将选项卡1 Neumorphic css翻译成选项卡2呢,就像从选项卡1滑到选项卡2一样? https://dribble.com/shots/10805627-neumorphic-tab

  • 问题内容: 我有一个常规的Twitter Bootstrap 3标签。我要做的就是控制 一个元素来控制多个容器。 在此示例中,当我更改标签时,仅第一个被更改。我要两个容器都改变,而不仅仅是第一个。 谢谢! 问题答案: 我将 data-target 属性添加到a元素并更改第二个tab内容中的id 我修改了这行 您的: 我的更新: 第二个选项卡内容,您的: 我的更新:

  • 使用JavaFX2.2,我有一个窗口,它主要由一个有4个选项卡的TabPane组成。用户可以更改任何选项卡中显示的数据,如果他们这样做了,我希望确保他们在保存/取消/更新数据之前不会更改该选项卡。特别是,我想确保他们不会选择不同的标签。如果他们试图退出或执行某个菜单项,我可以捕捉到,但我似乎找不到中断选项卡更改的方法。我可以捕捉到他们点击了一个新的选项卡(onSelectionChanged),并

  • 我们可以使用selenium ide将控件从一个选项卡转移到另一个选项卡吗

  • 过去几个月,我们公司一直在使用谷歌表单,我们的系统运行良好,但现在正在向Excel过渡。 Google Sheet中合并和排序所有数据的选项卡称为MASTER TEST,可在此处找到 在谷歌表单中,我们使用了以下公式: 数据被合并:是来自多个选项卡的数据。 结果是合并和排序的数据。如何在Excel中生成相同的报告? 基本上,我们接到的每个电话都记录在每个代表独有的标签中的电子表格中。我们有一个当前

  • 我想创建一个列表视图,只能通过鼠标进行多项选择(不按住ctrl或Shift) 单击某个项目应选择此项目。如果选择了其他项目,请将此新项目添加到所选列表中。以前选中此项目时,请取消选中它。 我不想保留选定的项目。selectionModel上有一个名为“selectIndices()”的方法,它只接受一个或多个整数,而不是整数列表。。。 有男孩有主意吗?