2017-01-10 3 views
0

В this question Мне было показано, как решить проблему смены свойства, изменив его обертывающий объект и, таким образом, не отправляя обновления, которые он изменил. Раствор использует ReactFX:Как выполнить пользовательскую привязку с Varation 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 для них путем связывания их свойства к свойствам графического интерфейса. Я делаю это так:

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(); 
    } 
} 

Я также открыт для разработки предложений, если это не является хорошим способом для создания представления для классов данных с использованием привязок.

+0

Что такое 'size' в последнем блоке кода ? Вам не нужно что-то вроде 'if (sizeVar.getValue(). IntValue() == 1) {...}' и т. Д.? –

+0

@James_D 'size' является свойством' Shape'. Я пробовал раньше с строкой, которую вы написали (принимая значение от 'sizeVar'), но не было никакой разницы. Я создам MCVE. я думал, что это просто неправильное использование. – Mark

+0

Да, я в замешательстве. Разумеется, 'size' является членом' Shape', но привязка находится вне класса 'Shape'. И это все равно не будет компилироваться, потому что 'size' является' IntegerProperty' и не может сравниться с '1'' == '. Если эта версия не работает, привязка, вероятно, собирает мусор. Мой ответ не работает? –

ответ

2

Val и Var являются «наблюдаемыми монадиями» (думаю, можно наблюдать Optional). Они либо пусты, либо имеют значение. Метод map работает так же, как Optional.map: если Val пуст, map приводит к пустому Val; в противном случае это приводит к Val, содержащему результат применения функции к исходному значению Val. Поэтому, если sizeVar оценивает null, результат сопоставления пуст Val (поэтому ваш материал установлен в null), даже не оценивая ваше выражение лямбды.

Для обработки 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(); 
    } 
} 
+0

Где сейчас находится "sizeVar'? – Mark

+0

@Mark это не так, но привязки, которые вы отправили, тоже не полагались на него (им просто нужно было знать, была ли форма пустой/пустой). Я предположил, что вы все еще нуждаетесь в этом где-то еще в своем коде. –

+0

хорошо, я понимаю. Большое спасибо! – Mark

 Смежные вопросы

  • Нет связанных вопросов^_^