我有一个简单的基于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上通过对象属性的自动更新功能并没有按照我最初希望的方式工作。当更新同一视图中的对象时,这很好,但当一个人试图从一个视图(如一个选项卡)更新对象,并在另一个视图(即不同的选项卡)中获得更新结果时,它会崩溃。
解决方案似乎是从主控制器访问相关选项卡的控制器视图,方法是在主控制器中嵌入对选项卡控制器的引用,然后通过嵌入式选项卡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()”的方法,它只接受一个或多个整数,而不是整数列表。。。 有男孩有主意吗?