如何使用ReactFX的Var进行自定义绑定?



在这个问题中,我展示了如何通过更改其包装对象来处理属性更改的问题,从而不发送它更改的更新。一个解决方案是使用ReactFX:

class Cell {
    private final ObjectProperty<Shape> shape = new SimpleObjectProperty<>(new Shape());
    // all getters and setterts
    public static class Shape {
        private final IntegerProperty size = new SimpleIntegerProperty(0);
        // all getters and setterts
    }
    public static void main(String[] args) {
        Var<Number> sizeVar = Val.selectVar(cell.shapeProperty(), Shape::sizeProperty);
        sizeVar.addListener(
            (obs, oldSize, newSize) -> System.out.println("Size changed from "+oldSize+" to "+newSize));
}

因此,现在如果shape属性本身发生变化,它也会触发size的变化(除非新形状具有相同的大小)。但是现在我想使用自定义绑定绑定到属性,并且我有一个问题,下面解释了一个问题。

我的数据类是这些:

class Cell {
    private final ObjectProperty<Shape> shape = new SimpleObjectProperty<>();
    public final ObjectProperty<Shape> shapeProperty() { return shape; }
    public final Shape getShape()                      { return shapeProperty().get(); }
    public final void setShape(Shape shape)            { shapeProperty().set(shape); }
    // other properties
}
class Shape {
    private final IntegerProperty size = new SimpleIntegerProperty();
    public final IntegerProperty sizeProperty() { return size; }
    public final int getSize()                  { return size.get(); }
    public final void setSize(int size)         { sizeProperty().set(size); }
    // other properties
}

我想通过将它们的属性绑定到 GUI 属性来为它们创建一个 GUI 表示。我是这样做的:

class CellRepresentation extends Group {
    private final Cell cell;
    CellRepresentation(Cell cell) {
        this.cell = cell;
        getChildren().add(new ShapeRepresentation() /*, other representations of things in the cell*/);
    }
    private class ShapeRepresentation extends Cylinder {
        ObjectProperty<Shape> shape;
        private ShapeRepresentation() {
            super(100, 100);
            shape = new SimpleObjectProperty<Shape>(cell.getShape());
            shape.bind(cell.shapeProperty());
            Var<Number> sizeVar = Val.selectVar(cell.shapeProperty(), Shape::sizeProperty);
            // THIS WILL WORK
            materialProperty().bind(Bindings.createObjectBinding(() -> {
                if (shape.get() == null)
                    return new PhongMaterial(Color.TRANSPARENT);
                return new PhongMaterial(Color.RED);
            }, sizeVar));
            // THIS WILL NOT WORK
            materialProperty().bind(sizeVar.map(n -> {
                if (shape.get() == null)
                    return new PhongMaterial(Color.TRANSPARENT);
                return new PhongMaterial(Color.RED);
            }));
        }
    }
    // the other representations of things in the cell
}

当我运行下面的代码时,绑定的第一个选项将创建一个透明的圆柱体。第二个选项将创建一个白色(默认颜色)圆柱体。我不知道为什么会这样。

public class Example extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    @Override
    public void start(Stage stage) throws Exception {
        Cell cell = new Cell();
        CellRepresentation cellRep = new CellRepresentation(cell);
        Group group = new Group(cellRep);
        Scene scene = new Scene(group, 200, 200, Color.AQUA);
        stage.setScene(scene);
        stage.show();
    }
}

如果这不是使用绑定为数据类创建表示的好方法,我也愿意接受设计建议。

ValVar是"可观察的monadics"(想想可观察的Optional s)。它们要么为空,要么保存一个值。map 方法的工作方式就像 Optional.map 一样:如果Val为空,则map导致空Val;否则,它会产生一个包含将函数应用于原始Val值的结果的Val。因此,如果sizeVar的计算结果为 null ,则映射会导致空Val(因此您的材料设置为 null ),甚至没有计算您的 lambda 表达式。

要处理null(即空Val),您应该使用orElse或类似的方法:

private class ShapeRepresentation extends Cylinder {
    Val<Shape> shape;
    private ShapeRepresentation() {
        super(100, 100);
        shape = Val.wrap(cell.shapeProperty());
        Var<Number> sizeVar = shape.selectVar(Shape::sizeProperty);
        // THIS WILL WORK
        materialProperty().bind(shape
            .map(s -> new PhongMaterial(Color.RED))
            .orElseConst(new PhongMaterial(Color.TRANSPARENT)));
        // SO WILL THIS
        materialProperty().bind(sizeVar
                .map(n -> {
                    if (n.intValue() == 1) return new PhongMaterial(Color.RED) ;
                    if (n.intValue() == 2) return new PhongMaterial(Color.BLUE) ;
                    return new PhongMaterial(Color.WHITE);
                })
                .orElseConst(new PhongMaterial(Color.TRANSPARENT)));
    }
}

更新了测试示例:

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Example extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    @Override
    public void start(Stage stage) throws Exception {
        Cell cell = new Cell();
        CellRepresentation cellRep = new CellRepresentation(cell);
        Group group = new Group(cellRep);
        ComboBox<Integer> sizeCombo = new ComboBox<>();
        sizeCombo.getItems().addAll(0, 1, 2);
        Shape shape = new Shape();
        shape.sizeProperty().bind(sizeCombo.valueProperty());

        CheckBox showShape = new CheckBox("Show shape");
        cell.shapeProperty().bind(Bindings.when(showShape.selectedProperty()).then(shape).otherwise((Shape)null));
        HBox controls = new HBox(5, showShape, sizeCombo);
        controls.setPadding(new Insets(5));
        BorderPane root = new BorderPane(group, controls, null, null, null);
        root.setBackground(null);
        Scene scene = new Scene(root, 400, 400, Color.AQUA);
        stage.setScene(scene);
        stage.show();
    }
}

相关内容

  • 没有找到相关文章

最新更新