因为这是有关设计的问题,所以我将先说一下我拥有和想要的东西。
我有 一个使用合成的设计。一个Cell对象包含Shape和一个Background对象(此示例中为定制对象)。这2个中的每个都有定义它们的自己的数据。这是代码示例:
Cell
Shape
Background
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中的更改:
我的问题 是,哪些类成员需要成为我绑定到的JavaFX属性。
这就是我的想法:“叶子”属性(大小,颜色,位置…)必须是属性,以便我可以将它们绑定到GUI属性。但是我是否也需要设置形状和背景对象属性?屏幕上只有它们的属性具有“ Actual”表示。理想情况下,如果Shape发生变化,那么它的所有属性都将告诉它们它们的绑定可能已经更改(也许颜色没有变化,但是尺寸却发生了变化)。但这并不是这样- 即使Shape的颜色可以在Shape改变时改变,Color属性也不能告诉它绑定的任何东西 。
使Cell成为具有许多单元格的较大图片中的属性等的方法也是如此:属性的属性委派更改。
因此,我想到了使Shape和Background也成为属性,InvalidationListener并向其注册会更新其属性。这似乎不太对劲,因为我认为在对属性的所有支持下,将有一种方法可以做我想做的事情。
InvalidationListener
有人可以建议一种方法吗?
仅使用标准JavaFX API,您就可以利用这些Bindings.selectXXX方法来观察“属性”。
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> shape = new SimpleObjectProperty<>(new Shape()); public final ObjectProperty<Shape> 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> 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<Color> 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),则绑定会生成烦人且冗长的警告消息(即使这被认为是受支持的用例)。
cel.getShape()
Tomas Mikula在他的ReactFX库中有一个更现代的实现,请参阅此帖子以获取描述。使用ReactFX,您将执行以下操作:
public static void main(String[] args) { Cell cell = new Cell(); Var<Number> 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); }
最后,如果要创建单元格列表,则可以创建一个ObservableList指定的extractor。提取器是将列表中的每个元素(每个Cell)映射到Observables 数组的函数。如果这些Observable更改中有任何更改,列表将触发更新事件。所以你可以做
ObservableList
extractor
Observable
ObservableList<Cell> cellList = FXCollections.observableArrayList(cell -> new Observable[] {Bindings.selectInteger(cell.shapeProperty(), "size")});
使用标准API,或者
ObservableList<Cell> cellList = FXCollections.observableArrayList(cell -> new Observable[] {Val.selectVar(cell.shapeProperty(), Shape::sizeProperty)});
使用ReactFX。然后,只需将a添加ListChangeListener到列表中,大小会发生变化(或形状更改为其他大小的新形状)时,系统会通知它。您可以根据需要在返回数组中添加尽可能多的可观察对象,这些可观察对象是单元格的属性。
ListChangeListener