javafx在具有绑定

时间:2015-11-22 05:18:25

标签: javafx binding java-8

我有点卡住了,也许有人可以提供帮助。我希望在某些字段发生变化时计算出“总计”。

我在excel中制作了一个原型来说明:

Mockup of two tables, Products and Summary

因此,无论何时更改价格或数量,都会重新计算总成本,并且还应更新摘要,包括“最终总计”。我已经使用了绑定并且它们不起作用,除非首次创建摘要项目,然后finalTotal字段不会重新计算。

我有两个类,一个名为Product,其中包含属性productNamepriceEachquantityproductTotal。每当设置priceEachquantity属性时,都会调用calculateTotal()方法来更新productTotal。 这是代码:

import javafx.beans.property.*;
import javafx.beans.property.SimpleFloatProperty;
import javafx.beans.property.StringProperty;

public class Product {
   private final StringProperty productName;
   private final FloatProperty priceEach;
   private final IntegerProperty quantity;
   private  FloatProperty productTotal;

   public Product(){
      this.productName = new SimpleStringProperty("Product Name");
      this.priceEach = new SimpleFloatProperty(0);
      this.quantity = new SimpleIntegerProperty(0);
      this.productTotal = new SimpleFloatProperty(0);
   }

   public Product(String name, float priceEach, int quantity){
      this.productName = new SimpleStringProperty(name);
      this.priceEach = new SimpleFloatProperty(priceEach);
      this.quantity = new SimpleIntegerProperty(quantity);
      this.productTotal = calculateProductTotal();
   }

   private FloatProperty calculateProductTotal() {
      return new SimpleFloatProperty(priceEach.get()*quantity.get());
   }

   public void setProductName(String name){
      productName.set(name);
   }

   public void setPriceEach(float price){
      priceEach.set(price);
      productTotal = calculateProductTotal();
   }

   public void setQuantity(int number){
      quantity.set(number);
      productTotal = calculateProductTotal();
   }

   public String getProductName(){ return productName.get(); }
   public float getPriceEach(){ return priceEach.get(); }
   public int getQuantity(){ return quantity.get(); }
   public float getProductTotal(){ return productTotal.get(); }

   public StringProperty productNameProperty() { return productName;}
   public FloatProperty priceEachProperty() { return priceEach;}
   public IntegerProperty quantityProperty() { return quantity;}
   public FloatProperty productTotalProperty() { return productTotal;}

   @Override
   public String toString(){
      return " Product: " + getProductName() +
              ", Price: " + getPriceEach() +
              ", Quantity: " + getQuantity() +
              ", Total cost: " + getProductTotal();
   }     
}

我还有一个Summary类,它有两个属性,它们单向绑定到Products类中的相应属性。这就是问题所在。

import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class Summary {
   private FloatProperty finalTotal;
   public ObservableList<Item> itemsSummary = FXCollections.observableArrayList(); 

   public Summary(){
      finalTotal=new SimpleFloatProperty(0);
   }
   public Item add()
   {
      Item newItem = new Item(); 
      itemsSummary.add(newItem);
      newItem.productTotalProperty().addListener((obs, oldAmount, newAmount) -> {
            System.out.println("Item total changed from "+oldAmount+" to "+newAmount);
            calculateTotal();
            System.out.println("Final total: "+finalTotal);
         });
      return newItem;    
   } 

   public class Item{
      private final StringProperty productName;
      private  FloatProperty productTotal;

      public Item(){
         this.productName = new SimpleStringProperty("");
         this.productTotal =  new SimpleFloatProperty(-1);    
      }
      public void setProductName(String name) { productName.set(name); }
      public void setProductTotal(float total) { productTotal.set(total); }
      public String getProductName() { return productName.get(); }
      public float getProductTotal() { return productTotal.get(); }
      public StringProperty productNameProperty() { return productName;}
      public FloatProperty productTotalProperty() { return productTotal;}     
   }

   private void calculateTotal(){
      float runningTotal=0;
      for (Item i: itemsSummary )
         runningTotal+= i.getProductTotal();
      finalTotal.set(runningTotal);
   }

   @Override
   public String toString(){
      String output= "";
      for(Item i: itemsSummary){
         String pn = i.getProductName();
         float  pt = i.getProductTotal();
         output = output +"\n"+ String.format("Product: %s,  Product cost: %.2f", pn, pt); 
      }
      return output+
                "\nFinal total: " + finalTotal.getValue();
   }
}

正如您所看到的,我有一个内部类Item包含数据(名称和小计)。我已经为newItem.productTotalProperty()添加了一个监听器,但这只在构造新Item时才起作用:当产品的字段发生更改时,其实例的总数会更新,但对Summary对象的绑定会停止工作,所以它的所有领域都没有改变。

以下是测试驱动程序代码:

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class BindingsPrototypeTestDriver {
   public static void main(String[] args)
   {
      Product A = new Product("Pencil", 1.50f, 300);
      Product B = new Product("Eraser", 0.50f, 200);
      Product C = new Product("Paper", 4.95f, 100);
      ObservableList<Product> products = FXCollections.observableArrayList(); 
      products.add(A);
      products.add(B);
      products.add(C);
      Summary summary = new Summary();
      for(Product p: products)
      {
         Summary.Item summaryItem = summary.add();
         summaryItem.productNameProperty().bind(p.productNameProperty());
         summaryItem.productTotalProperty().bind(p.productTotalProperty());
      }

             for(Product p: products){ System.out.println(p.toString()); }
             System.out.println(summary.toString());
      C.setQuantity(200);
            for(Product p: products){ System.out.println(p.toString()); }
            System.out.println(summary.toString());
   }   
}

这是输出:

run:
Item total changed from -1.0 to 450.0
Final total: FloatProperty [value: 450.0]
Item total changed from -1.0 to 100.0
Final total: FloatProperty [value: 550.0]
Item total changed from -1.0 to 494.99997
Final total: FloatProperty [value: 1045.0]
Product: Pencil, Price: 1.50, Quantity: 300, Total cost:  450.00
Product: Eraser, Price: 0.50, Quantity: 200, Total cost:  100.00
Product: Paper, Price: 4.95, Quantity: 100, Total cost:  495.00

Product: Pencil,  Product cost: 450.00
Product: Eraser,  Product cost: 100.00
Product: Paper,  Product cost: 495.00
Final total: 1045.0
Product: Pencil, Price: 1.50, Quantity: 300, Total cost:  450.00
Product: Eraser, Price: 0.50, Quantity: 200, Total cost:  100.00
Product: Paper, Price: 4.95, Quantity: 200, Total cost:  990.00

Product: Pencil,  Product cost: 450.00
Product: Eraser,  Product cost: 100.00
Product: Paper,  Product cost: 495.00
Final total: 1045.0

如您所见,当“Paper”实例的数量更改为200时,所有内容都会在Product实例中正确更新,但不会在摘要中...

我做错了什么?

1 个答案:

答案 0 :(得分:2)

您的代码存在一些问题。

您遇到的问题是您使用的productTotal属性不会更新,但每次更新时都会替换为新的属性对象。您的Product课程的用户无法知道这一点。实际上,用户希望属性getter总是返回相同的实例(并且你也期望也是这样)。

为了简单起见,不是创建新属性,而是为旧属性分配新值,或者只使用Bindings类:

public class Product {
   private final StringProperty productName;
   private final FloatProperty priceEach;
   private final IntegerProperty quantity;
   private final FloatProperty productTotal;

   public Product(){
       this("Product Name", 0, 0);
   }

   public Product(String name, float priceEach, int quantity){
      this.productName = new SimpleStringProperty(name);
      this.priceEach = new SimpleFloatProperty(priceEach);
      this.quantity = new SimpleIntegerProperty(quantity);
      this.productTotal = new SimpleFloatProperty();
      this.productTotal.bind(Bindings.multiply(this.quantity, this.priceEach));
   }

   public void setPriceEach(float price){
      priceEach.set(price);
   }

   ...

Summary class

  • 为什么要添加嵌套的Item类。您已经有Product课程。将数据存储在内存中两次并没有多大意义。你也依靠外部代码为你绑定价值......那个设计很差。
  • 更糟糕的是:您的itemsSummarypublic而非final,这意味着任何引用Summary实例的人都可以更改值{{1} }实例没有被告知这一变化。这不是问题的唯一原因是您不能使用Summary列表。实际上,您可以使用ObservableList替换代码中的所有列表,但这不会有所作为。

这就是我要做的事情:

  • 制作ArrayList itemsSummary甚至final并提供吸气剂
  • 使用private在列表项目中添加/删除列表。
  • 使用ListChangeListener s
  • 列表
Product

这些修改使public class Summary { private final FloatProperty finalTotal; public final ObservableList<Product> itemsSummary; private final ChangeListener<Number> itemTotalChangeListener = (observable, oldValue, newValue) -> { calculateTotal(); }; public Summary() { finalTotal = new SimpleFloatProperty(0); itemsSummary = FXCollections.observableArrayList(); itemsSummary.addListener((ListChangeListener.Change<? extends Product> c) -> { boolean modified = false; while (c.next()) { if (c.wasRemoved()) { modified = true; for (Product p : c.getRemoved()) { p.productTotalProperty().removeListener(itemTotalChangeListener); } } if (c.wasAdded()) { modified = true; for (Product p : c.getAddedSubList()) { p.productTotalProperty().addListener(itemTotalChangeListener); } } } if (modified) { calculateTotal(); } }); } ... 方法中的代码更简单:

main