【问题标题】:JavaFX program runs, but still getting exceptionJavaFX 程序运行,但仍然出现异常
【发布时间】:2018-04-20 09:16:35
【问题描述】:

我正在编写一个使用滑块计算小费的 JavaFX 程序。它工作得很好,但是当我改变滑块的值时,我得到了很多错误,比如Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: Label.text : A bound value cannot be set.

似乎我的错误与绑定到滑块有关,但不确定是什么问题,因为它按我的预期运行。任何指针将不胜感激。谢谢

这是程序:

TipCalculator.java

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class TipCalculator extends Application
{

    @Override
    public void start(Stage stage) throws Exception
    {
        Parent root = FXMLLoader.load(getClass().getResource("TipCalculator.fxml"));

        Scene scene = new Scene(root);
        stage.setTitle("Tip Calculator");
        stage.setScene(scene);
        stage.show();
    }

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

TipCalculatorController.java

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
import javafx.beans.property.Property;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;

public class TipCalculatorController
{
    private static final NumberFormat currency =
        NumberFormat.getCurrencyInstance();
    private static final NumberFormat percent =
        NumberFormat.getPercentInstance();

    private BigDecimal tipPercentage = new BigDecimal(0.15);

    @FXML
    private TextField amountTextField;

    @FXML
    private Label tipPercentageLabel;

    @FXML 
    private Slider tipPercentageSlider;

    @FXML
    private TextField tipTextField;

    @FXML
    private TextField totalTextField;

    @FXML
    private void initialize()
    {
        try
        {
            BigDecimal amount = new BigDecimal(amountTextField.getText());
            BigDecimal tip = amount.multiply(tipPercentage);
            BigDecimal total = amount.add(tip);

            tipTextField.setText(currency.format(tip));
            totalTextField.setText(currency.format(total));
        }
        catch (NumberFormatException ex)
        {
            amountTextField.setText("Enter amount");
            amountTextField.selectAll();
            amountTextField.requestFocus();
        }

        tipPercentageLabel.textProperty().bind           
            (tipPercentageSlider.valueProperty().asString("%.0f"));

        currency.setRoundingMode(RoundingMode.HALF_UP);

        tipPercentageSlider.valueProperty().addListener(
            new ChangeListener<Number>()
            {
                @Override
                public void changed(ObservableValue<? extends Number> ov,
                        Number oldValue, Number newValue)
                {
                    tipPercentage =
                            BigDecimal.valueOf(newValue.intValue() / 100.0);
                    tipPercentageLabel.setText(percent.format(tipPercentage));
                }
            }
        );
    }
}

TipCalculator.fxml

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

<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<GridPane hgap="8.0" vgap="8.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="TipCalculatorController">
  <columnConstraints>
    <ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="10.0" />
    <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
  </columnConstraints>
  <rowConstraints>
    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
      <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
  </rowConstraints>
   <children>
      <Label text="Amount" />
      <Label fx:id="tipPercentageLabel" text="15%" GridPane.rowIndex="1" />
      <Label text="Tip" GridPane.rowIndex="2" />
      <Label text="Total" GridPane.rowIndex="3" />
      <TextField fx:id="amountTextField" onAction="#initialize" GridPane.columnIndex="1" />
      <TextField fx:id="tipTextField" editable="false" focusTraversable="false" GridPane.columnIndex="1" GridPane.rowIndex="2" />
      <TextField fx:id="totalTextField" editable="false" focusTraversable="false" GridPane.columnIndex="1" GridPane.rowIndex="3" />
      <Slider fx:id="tipPercentageSlider" blockIncrement="5.0" max="30.0" onDragDetected="#initialize" value="15.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
   </children>
   <padding>
      <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
   </padding>
</GridPane>

【问题讨论】:

    标签: java javafx bigdecimal


    【解决方案1】:

    正如异常所说,无法设置绑定值。绑定的意义在于您声明一个属性应始终具有依赖于另一个属性的值。如果您可以直接设置绑定属性,您将能够违反您通过绑定指定的合同。

    所以当你这样做时

    tipPercentageLabel.textProperty().bind           
            (tipPercentageSlider.valueProperty().asString("%.0f"));
    

    您确保tipPercentageLabel 的文本属性将始终具有tipPercentageSlider.getValue() 的字符串值(格式为一位小数等)。如果您随后被允许设置 tipPercentageLabel 的文本,则可能违反此绑定,因此现在禁止这样做。

    因此,

        tipPercentageSlider.valueProperty().addListener(
            new ChangeListener<Number>()
            {
                @Override
                public void changed(ObservableValue<? extends Number> ov,
                        Number oldValue, Number newValue)
                {
                    tipPercentage =
                            BigDecimal.valueOf(newValue.intValue() / 100.0);
                    tipPercentageLabel.setText(percent.format(tipPercentage));
                }
            }
        );
    

    当滑块值改变时抛出异常,因为它试图显式设置标签的文本。

    绑定和侦听器实际上只是实现(或多或少)相同事物的两种方式(提供或获取一些格式)。所以这个问题的解决方案是你应该只有其中之一:要么删除绑定,要么删除监听器。就个人而言,我发现绑定方法更优雅(我更喜欢显式声明依赖项,而侦听器更机械化)。您的里程可能会有所不同。

    如果你想使用百分比格式,你可以通过绑定来实现

    tipPercentageLabel.textProperty().bind(Bindings.createStringBinding(() -> {
        tipPercentage = BigDecimal.valueOf(newValue.intValue() / 100.0);
        return percent.format(tipPercentage);
    }, tipPercentageSlider.valueProperty());
    

    或者你可以这样做

    tipPercentageLabel.textProperty().bind(tipPercentageSlider.asString("%.0f%%"));
    

    (格式字符串中的%% 给出"%" 符号)。

    【讨论】:

    • 谢谢,我没有意识到我正在尝试显式设置tipPercentageLabel。我最终删除了最后一行代码:tipPercentageLabel.setText(percent.format(tipPercentage));,我所有的错误都消失了。谢谢!
    • @greglorious_85 但是现在监听器实际上并没有做任何事情,所以你不妨完全删除它。
    • 我明白你在说什么。我不确定如何实现它。我尝试插入您建议的代码,但无法运行。
    • @greglorious_85 我的意思是你可以完全删除监听器。其余的只是向您展示使用绑定在文本中包含% 符号的方法(这似乎是您在侦听器中尝试做的事情)。
    • 但是我需要听者!这个练习的重点是真正有一个听众
    猜你喜欢
    • 2017-11-24
    • 2016-11-05
    • 1970-01-01
    • 2015-01-06
    • 1970-01-01
    • 2018-05-05
    • 2012-02-08
    • 1970-01-01
    • 2017-08-29
    相关资源
    最近更新 更多