属性属性有意义吗?

因为这是一个关于设计的问题,我将首先说出我拥有的和我想要的东西。

我有一个使用构图的设计。 Cell对象包含ShapeBackground对象(此示例为自定义对象)。 这两个中的每一个都有自己的数据来定义它们。 这是代码中的示例:

 class Cell { Shape shape; Background background; class Shape { int size; Color color; Point location; //... } class Background { Color color; String name; CoverType type; //... } } 

我还有一个需要代表许多单元格的GUI,我已经写了如何做(如何使用颜色,大小等来创建我想要的屏幕)。 它包括CellRepresentation,ShapeRepresentation和BackgroundRepresentation等类,它们的显示属性绑定到数据属性(我认为这称为模型和视图)。

我希望能够通过更改上述数据来表示GUI中的更改:

  • 用户可以(例如)右键单击形状并设置其颜色。 因此上面的数据发生了变化,需要在GUI中反映出变化。
  • 用户还可以更改整个形状(例如,从另一个单元格中复制粘贴)。 甚至整个细胞。 这些变化也需要反映在GUI中。

我的问题是哪个类成员需要是我绑定的JavaFX属性。

这就是我的想法:“叶子”属性(大小,颜色,位置……)必须是属性,所以我可以将GUI属性绑定到它们。 但我是否也需要制作形状和背景对象属性? 只有他们的属性在屏幕上有“实际”表示。 理想情况下,我会喜欢它,如果Shape发生变化,那么它的所有属性都会告诉它们的绑定它们可能已经改变了(可能颜色没有,但尺寸确实如此)。 但它不会以这种方式工作 – 即使Shape变化时Color的颜色可以改变Color属性也不会告诉它改变了什么绑定它

同样适用于在较大的图片中制作Cell属性,其中有许多单元格等等:属性委托更改的属性。

所以我想到使Shape和Background也属性并注册一个InvalidationListener来更新它们的属性。 这似乎并不正确,因为我认为,通过对财产的所有支持,将有一种方法来做我想要的。

有人可以建议一种方法吗?

使用标准JavaFX API,您可以利用Bindings.selectXXX方法来观察“属性的属性”。

例如:

 import javafx.beans.binding.Bindings; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.scene.paint.Color; public class Cell { private final ObjectProperty shape = new SimpleObjectProperty<>(new Shape()); public final ObjectProperty shapeProperty() { return this.shape; } public final Cell.Shape getShape() { return this.shapeProperty().get(); } public final void setShape(final Cell.Shape shape) { this.shapeProperty().set(shape); } public static class Shape { private final IntegerProperty size = new SimpleIntegerProperty(0); private final ObjectProperty color = new SimpleObjectProperty<>(Color.BLACK); public final IntegerProperty sizeProperty() { return this.size; } public final int getSize() { return this.sizeProperty().get(); } public final void setSize(final int size) { this.sizeProperty().set(size); } public final ObjectProperty colorProperty() { return this.color; } public final javafx.scene.paint.Color getColor() { return this.colorProperty().get(); } public final void setColor(final javafx.scene.paint.Color color) { this.colorProperty().set(color); } } public static void main(String[] args) { Cell cell = new Cell(); Bindings.selectInteger(cell.shapeProperty(), "size").addListener( (obs, oldSize, newSize) -> System.out.println("Size changed from "+oldSize+" to "+newSize)); cell.getShape().setSize(10); cell.setShape(new Shape()); Shape s = new Shape(); s.setSize(20); cell.setShape(s); } } 

将产生(期望的)输出

 Size changed from 0 to 10 Size changed from 10 to 0 Size changed from 0 to 20 

这个API有一些遗留的感觉,因为它依赖于将属性名称作为字符串传递,因此不是类型安全的,并且在编译时无法检查。 此外,如果任何中间属性为null(例如,如果cel.getShape()在此示例中返回null),则绑定会生成烦人且详细的警告消息(即使这应该是受支持的用例)。

Tomas Mikula在他的ReactFX库中有一个更现代的实现,请参阅这篇文章以获得描述。 使用ReactFX,您可以:

 public static void main(String[] args) { Cell cell = new Cell(); Var size = Val.selectVar(cell.shapeProperty(), Shape::sizeProperty); size.addListener( (obs, oldSize, newSize) -> System.out.println("Size changed from "+oldSize+" to "+newSize)); cell.getShape().setSize(10); cell.setShape(new Shape()); Shape s = new Shape(); s.setSize(20); cell.setShape(s); } 

最后,如果要创建单元格列表,可以创建一个指定extractorObservableList 。 提取器是将列表中的每个元素(每个Cell )映射到Observable数组的函数。 如果任何Observable更改,列表将触发更新事件。 所以你可以做到

 ObservableList cellList = FXCollections.observableArrayList(cell -> new Observable[] {Bindings.selectInteger(cell.shapeProperty(), "size")}); 

使用标准API,或

 ObservableList cellList = FXCollections.observableArrayList(cell -> new Observable[] {Val.selectVar(cell.shapeProperty(), Shape::sizeProperty)}); 

使用ReactFX。 然后只需将ListChangeListener添加到列表中,如果大小更改(或者形状更改为具有不同大小的新形状),将通知它。 您可以根据需要添加尽可能多的可观察对象,这些对象是返回数组中单元格的属性(或属性属性)。