ScalaFXML应用程序:在CheckBox上运行时向表单添加组件单击

时间:2016-05-30 19:47:55

标签: scala scalafx

我正在为家庭成员创建一个订单管理系统。当用户选择订单是用于蛋糕,饼干,蛋糕还是其他时,我需要应用程序能够在运行时自行更新。

我一直在尝试作为解决方案是在表单中添加一个空窗格,然后在单击时,表单将使用位于另一个FXML文件中的组件填充窗格。问题是:我在控制器中的字段都设置为null,所以当我尝试更新窗格时,我得到一个空指针异常。我无法弄清楚为什么我的所有字段都为空,但我怀疑它是@sfxml注释与我设置的方式不相符。也许有一种更好的方法可以完全做到这一点。任何帮助表示赞赏。这是我到目前为止所拥有的;我将尝试给出一个可证明的例子。我计划添加组件的区域通过表单中的注释可见,位于复选框下方:

表格:Form.FXML

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

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane id="rootLayout" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
            prefHeight="500.0" prefWidth="500.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
            fx:controller="controller.FormController">
    <center>
        <VBox prefHeight="200.0" prefWidth="100.0" BorderPane.alignment="CENTER">
            <children>
                <TilePane maxHeight="119.0" minHeight="100.0" prefHeight="119.0" prefWidth="485.0">
                    <children>
                        <HBox maxHeight="30.0" maxWidth="485.0" minHeight="10.0" prefHeight="14.0" prefWidth="485.0"
                              TilePane.alignment="CENTER">
                            <children>
                                <TextField fx:id="fNameField" maxHeight="30.0" minHeight="17.0" prefHeight="18.0"
                                           prefWidth="156.0" promptText="first name"/>
                                <TextField fx:id="lNameField" maxHeight="30.0" minHeight="30.0" promptText="last name"/>
                                <TextField fx:id="dateField" maxHeight="30.0" minHeight="26.0" prefHeight="30.0"
                                           prefWidth="151.0" promptText="due date"/>
                            </children>
                        </HBox>
                        <HBox maxHeight="30.0" maxWidth="485.0" minHeight="10.0" prefHeight="14.0" prefWidth="485.0"
                              TilePane.alignment="CENTER">
                            <children>
                                <TextField fx:id="phoneNumber" maxHeight="30.0" minHeight="17.0" prefHeight="18.0"
                                           prefWidth="156.0" promptText="phone number"/>
                                <TextField fx:id="email" maxHeight="30.0" minHeight="30.0" prefHeight="30.0"
                                           prefWidth="331.0" promptText="e-mail"/>
                            </children>
                        </HBox>

                        <TextField fx:id="streetField" maxHeight="30.0" minHeight="30.0" promptText="street"/>
                        <HBox maxHeight="44.0" minHeight="0.0" prefHeight="27.0" prefWidth="485.0">
                            <children>
                                <TextField fx:id="cityField" maxHeight="30.0" minHeight="30.0" prefHeight="30.0"
                                           prefWidth="374.0" promptText="city"/>
                                <TextField fx:id="stateField" maxHeight="30.0" minHeight="30.0" prefHeight="30.0"
                                           prefWidth="109.0" promptText="state"/>
                                <TextField fx:id="zipField" maxHeight="30.0" minHeight="30.0" prefHeight="30.0"
                                           prefWidth="136.0" promptText="zip"/>
                            </children>
                        </HBox>
                    </children>
                </TilePane>
                <HBox maxHeight="65.0" minHeight="30.0" prefHeight="33.0" prefWidth="485.0" spacing="95.0">
                    <children>
                        <CheckBox fx:id="cakeCB" mnemonicParsing="false" text="CK" onAction="#checkboxSelected">
                            <HBox.margin>
                                <Insets top="8.0"/>
                            </HBox.margin>
                        </CheckBox>
                        <CheckBox  fx:id="cookiesCB" mnemonicParsing="false" text="CO">
                            <HBox.margin>
                                <Insets top="8.0"/>
                            </HBox.margin>
                        </CheckBox>
                        <CheckBox  fx:id="cupcakesCB" mnemonicParsing="false" text="CC">
                            <HBox.margin>
                                <Insets top="8.0"/>
                            </HBox.margin>
                        </CheckBox>
                        <CheckBox  fx:id="otherCB" mnemonicParsing="false" text="O">
                            <HBox.margin>
                                <Insets top="8.0"/>
                            </HBox.margin>
                        </CheckBox>
                    </children>
                    <padding>
                        <Insets left="18.0"/>
                    </padding>
                </HBox>
                <HBox>

                 <!--


                 included data here-->
                <Pane id="selectionQuestions" />
                </HBox>
                <TextArea fx:id="notes" prefHeight="200.0" prefWidth="200.0"/>
                <HBox alignment="BOTTOM_RIGHT" maxHeight="30.0" minHeight="30.0" prefHeight="100.0" prefWidth="200.0">
                    <children>
                        <Button id="submitOrderButton" mnemonicParsing="false" onAction="#submitOrder" text="submit"/>
                    </children>
                </HBox>
            </children>
        </VBox>
    </center>
    <padding>
        <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
    </padding>
</BorderPane>

要添加到HBox区域:check_box_selected.fxml

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

  <?import javafx.scene.layout.HBox?>
  <?import javafx.scene.control.Label?>  

    <HBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" minWidth="30" minHeight="30">
      <Label text="Here's my selected checkbox label" />
    </HBox>

FormController.scala:

package controller

import javafx.event.ActionEvent
import javafx.scene._
import javafx.stage.Stage

import javafx.scene.layout.Pane
import javafx.scene.control.CheckBox
import scalafxml.core.macros.sfxml
import scalafxml.core.{FXMLView, NoDependencyResolver}
import javafx.fxml.FXMLLoader


@sfxml
class FormController(private val selectionQuestions: Pane,
                     private val cakeCB: CheckBox,
                     private val cookiesCB: CheckBox,
                     private val cupcakesCB: CheckBox,
                     private val otherCB: CheckBox) {

  def submitOrder(actionEvent: ActionEvent) {
    val source = actionEvent.getSource.asInstanceOf[Node].getScene.getWindow.asInstanceOf[Stage]
    source.hide;
  }

  def checkboxSelected(actionEvent: ActionEvent) {
    val source = actionEvent.getSource
    val box: CheckBox = source.asInstanceOf[CheckBox]

    box.getId match {
      case "cakeCB" => {
        val resource = getClass.getResource("/scala/cake_box_selected.fxml")
        val root = FXMLView(resource, NoDependencyResolver)

        //This is the null pointer => 
        selectionQuestions.getChildren.clear
        selectionQuestions.getChildren.add(root)
      }
    }
  }

}

1 个答案:

答案 0 :(得分:1)

您必须在控制器类的构造函数中使用控件的ScalaFX版本。所以简单而不是

import javafx.scene.layout.Pane
import javafx.scene.control.CheckBox

使用

import scalafx.scene.layout.Pane
import scalafx.scene.control.CheckBox

这是应该修复的scalafxml库的限制。 (为它创建了一个问题:https://github.com/vigoo/scalafxml/issues/19

<强> EDITED

正如您在评论中所解释的,当使用ScalaFX类型时,在尝试使用ActionEvent的源时,它会因强制转换异常而失败。此属性具有类型Object,并且包含对JavaFX控件本身的引用,该控件具有类型javafx.scene.control.CheckBox。由于宏的限制,这可以隐式转换为您必须使用的ScalaFX包装。

所以解决方案是:

  • 到处使用ScalaFX类型
  • 除了必须使用JavaFX类型的强制转换之外 - 编译器将负责转换。

示例:

import scalafx.event.ActionEvent
import scalafx.scene.control.{CheckBox, ComboBox, TextField}

// ...

  def testCheckBoxAction(event: ActionEvent): Unit = {
    val source: CheckBox = event.source.asInstanceOf[javafx.scene.control.CheckBox]
    // ...
  }