JavaFX - Java.lang.RuntimeException无法设置绑定值

时间:2017-01-03 17:00:00

标签: javafx binding

我完成了一个Small Java FX项目,我为我的一个列使用了多重绑定,以便它可以即时更新。我遇到的问题是我无法使用绑定打开XML文件,因为它在标题中给出了上述错误。这是我的项目代码

public class Items {

    private final StringProperty name;
    private final IntegerProperty quantity;
    private final DoubleProperty price;
    private final DoubleProperty total;

      public Items() {
          this(null,0,0); 
          }


    public Items(String name, int quantity, double price) {
        this.name = new SimpleStringProperty(name);
        this.quantity = new SimpleIntegerProperty(quantity);
        this.price = new SimpleDoubleProperty(price);
        this.total = new SimpleDoubleProperty();
        NumberBinding multiplication = Bindings.multiply(
        this.priceDoubleProperty(), this.quantityIntegerProperty());
        this.totalProperty().bind(multiplication);
    }

    public String getName() {
        return name.get();
    }

    public void setName(String name) {
        this.name.set(name);
    }

    public StringProperty NameProperty() {
        return name;
    }

    public int getQuantity() {
        return quantity.get();
    }

    public void setQuantity(int quantity) {
        this.quantity.set(quantity);
    }

    public IntegerProperty quantityIntegerProperty() {
        return quantity;
    }

    public double getPrice() {
        return price.get();
    }

    public void setPrice(double price) {
        this.price.set(price);
    }

    public DoubleProperty priceDoubleProperty() {
        return price;
    }

    public double getTotal() {
        return total.get();
    }

    public void setTotal(double total) {
        this.total.set(total);

    }

    public DoubleProperty totalProperty() {
        return total;
    }

}

这是MainApp代码

public class MainApp extends Application {

    private Stage primaryStage;
    private BorderPane rootLayout;
    public static ObservableList<Items> itemData = FXCollections
            .observableArrayList();

    public MainApp() {
        // Add some sample data
        itemData.add(new Items("Hans", 0, 0));
        itemData.add(new Items("Ruth", 0, 0));
    }

    /**
     * Returns the data as an observable list of Persons.
     * 
     * @return
     */
    public ObservableList<Items> getItemData() {
        return itemData;
    }

    @Override
    public void start(Stage primaryStage) {
        this.primaryStage = primaryStage;
        this.primaryStage.setTitle("Shopping Basket");
        primaryStage.setMinHeight(400);
        primaryStage.setMinWidth(600);
        primaryStage.setMaxHeight(600);
        primaryStage.setMaxWidth(700);
        this.primaryStage.getIcons().add(new Image("file:resources/images/shopping_basket_32.png"));
        initRootLayout();
        showBasketOverview();

    }

    /**
     * Initializes the root layout.
     */
    public void initRootLayout() {
        try {
            // Load root layout from fxml file.
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(MainApp.class
                    .getResource("view/RootLayout.fxml"));
            rootLayout = (BorderPane) loader.load();

            // Show the scene containing the root layout.
            Scene scene = new Scene(rootLayout);
            primaryStage.setScene(scene);

            // Give the controller access to the main app.
            RootLayoutController controller = loader.getController();
            controller.setMainApp(this);

            primaryStage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Try to load last opened person file.
        File file = getItemFilePath();
        if (file != null) {
            loadItemDataFromFile(file);
        }
    }

    /**
     * Opens a dialog to edit details for the specified person. If the user
     * clicks OK, the changes are saved into the provided person object and true
     * is returned.
     * 
     * @param person
     *            the person object to be edited
     * @return true if the user clicked OK, false otherwise.
     */

    /**
     * Shows the person overview inside the root layout.
     */
    public void showBasketOverview() {
        try {
            // Load person overview.
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(MainApp.class
                    .getResource("view/BasketOverview.fxml"));
            AnchorPane BasketOverview = (AnchorPane) loader.load();

            // Set person overview into the center of root layout.
            rootLayout.setCenter(BasketOverview);

            // Give the controller access to the main app.
            BasketOverviewController controller = loader.getController();
            controller.setMainApp(this);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Returns the main stage.
     * 
     * @return
     */
    public Stage getPrimaryStage() {
        return primaryStage;
    }

    public boolean showBasketEditDialog(Items item) {
        try {
            // Load the fxml file and create a new stage for the popup dialog.
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(MainApp.class
                    .getResource("view/BasketEditDialog.fxml"));
            AnchorPane page = (AnchorPane) loader.load();

            // Create the dialog Stage.
            Stage dialogStage = new Stage();
            dialogStage.setTitle("Edit Basket");
            dialogStage.initModality(Modality.WINDOW_MODAL);
            dialogStage.initOwner(primaryStage);
            Scene scene = new Scene(page);
            dialogStage.setScene(scene);

            // Set the person into the controller.
            BasketEditDialogController controller = loader.getController();
            controller.setDialogStage(dialogStage);
            controller.setItems(item);

            // Show the dialog and wait until the user closes it
            dialogStage.showAndWait();

            return controller.isOkClicked();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    public File getItemFilePath() {
        Preferences prefs = Preferences.userNodeForPackage(MainApp.class);
        String filePath = prefs.get("Filepath", null);
        if (filePath != null) {
            return new File(filePath);
        } else {
            return null;
        }
    }

    public void setItemFilePath(File file) {
        Preferences prefs = Preferences.userNodeForPackage(MainApp.class);
        if (file != null) {
            prefs.put("filePath", file.getPath());

            primaryStage.setTitle("Shopping Basket - " + file.getName());
        } else {
            prefs.remove("filePath");
            primaryStage.setTitle("Shopping Basket");
        }
    }

    public void loadItemDataFromFile(File file) {
        try {
            JAXBContext context = JAXBContext
                    .newInstance(BasketListWrapper.class);
            Unmarshaller um = context.createUnmarshaller();

            BasketListWrapper wrapper = (BasketListWrapper) um.unmarshal(file);

            itemData.clear();
            itemData.addAll(wrapper.getItems());

            setItemFilePath(file);

        } catch (Exception e) {
            Alert alert = new Alert(AlertType.ERROR);
            alert.setTitle("Error");
            alert.setHeaderText(e + "Could not load data"); 
            alert.setContentText("Could not load data from file:\n"
                    + file.getPath());


            alert.showAndWait();

        }
    }

    public void saveItemDataToFile(File file) {
        try {
            JAXBContext context = JAXBContext
                    .newInstance(BasketListWrapper.class);
            Marshaller m = context.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

            BasketListWrapper wrapper = new BasketListWrapper();
            wrapper.setItems(itemData);

            m.marshal(wrapper, file);

            setItemFilePath(file);
        } catch (Exception e) {
            Alert alert = new Alert(AlertType.ERROR);
            alert.setTitle("Error");
            alert.setHeaderText("Could not load data");
            System.out.println(e);
            alert.setContentText("Could not load data from file:\n"
                    + file.getPath());

            alert.showAndWait();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

这是错误代码

java.lang.RuntimeException: A bound value cannot be set.
    at javafx.beans.property.DoublePropertyBase.set(Unknown Source)
    at sb.basket.model.Items.setTotal(Items.java:83)
    at sb.basket.model.Items$JaxbAccessorM_getTotal_setTotal_double.set(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.reflect.Accessor.receive(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.endElement(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.endElement(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(Unknown Source)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(Unknown Source)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(Unknown Source)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(Unknown Source)
    at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(Unknown Source)
    at sb.basket.MainApp.loadItemDataFromFile(MainApp.java:201)
    at sb.basket.view.RootLayoutController.handleOpen(RootLayoutController.java:50)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.reflect.misc.Trampoline.invoke(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.reflect.misc.MethodUtil.invoke(Unknown Source)
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(Unknown Source)
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(Unknown Source)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
    at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source)
    at javafx.event.Event.fireEvent(Unknown Source)
    at javafx.scene.control.MenuItem.fire(Unknown Source)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(Unknown Source)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren$343(Unknown Source)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(Unknown Source)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
    at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source)
    at javafx.event.Event.fireEvent(Unknown Source)
    at javafx.scene.Scene$MouseHandler.process(Unknown Source)
    at javafx.scene.Scene$MouseHandler.access$1500(Unknown Source)
    at javafx.scene.Scene.impl_processMouseEvent(Unknown Source)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Unknown Source)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$355(Unknown Source)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(Unknown Source)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(Unknown Source)
    at com.sun.glass.ui.View.handleMouseEvent(Unknown Source)
    at com.sun.glass.ui.View.notifyMouse(Unknown Source)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$149(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

1 个答案:

答案 0 :(得分:1)

JavaFX绑定表明属性的值应始终等于某个表达式。具体来说,在你的代码中

NumberBinding multiplication = Bindings.multiply(
    this.priceDoubleProperty(), this.quantityIntegerProperty());
this.totalProperty().bind(multiplication);

您声明total应始终为price x quantity

设置绑定值是违法的(因为将其设置为某个值会破坏绑定,即违反绑定所隐含的合同)。

实际上,您已使total成为&#34;派生属性`:其值完全由其他值确定(在同一对象中,在本例中)。因此:

  1. 您不应该坚持total,因为这样做是多余的。
  2. 您不应公开任何可以设置总数的API。
  3. 发生异常是因为当您通过JAXB解组XML时,反序列化过程会尝试设置总计(存储在XML文件中)。我对JAXB不太熟悉,但以下内容应该有效(请注意,这与以前存储项目的XML文件不兼容;我也修复了一些方法名称,以便它们与JavaFX属性模式匹配):

    public class Items {
    
        private final StringProperty name;
        private final IntegerProperty quantity;
        private final DoubleProperty price;
        private final ReadOnlyDoubleWrapper total;
    
          public Items() {
              this(null,0,0); 
              }
    
    
        public Items(String name, int quantity, double price) {
            this.name = new SimpleStringProperty(name);
            this.quantity = new SimpleIntegerProperty(quantity);
            this.price = new SimpleDoubleProperty(price);
            this.total = new ReadOnlyDoubleWrapper();
            NumberBinding multiplication = Bindings.multiply(
            this.priceDoubleProperty(), this.quantityProperty());
            this.total.bind(multiplication);
        }
    
        public String getName() {
            return name.get();
        }
    
        public void setName(String name) {
            this.name.set(name);
        }
    
        public StringProperty nameProperty() {
            return name;
        }
    
        public int getQuantity() {
            return quantity.get();
        }
    
        public void setQuantity(int quantity) {
            this.quantity.set(quantity);
        }
    
        public IntegerProperty quantityProperty() {
            return quantity;
        }
    
        public double getPrice() {
            return price.get();
        }
    
        public void setPrice(double price) {
            this.price.set(price);
        }
    
        public DoubleProperty priceProperty() {
            return price;
        }
    
        @XmlTransient
        public double getTotal() {
            return total.get();
        }
    
        public ReadOnlyDoubleProperty totalProperty() {
            return total.getReadOnlyProperty();
        }
    
    }