2016-11-19 5 views
1

Я столкнулся с этой проблемой в приложении большего масштаба, где некоторые пользовательские привязки не обновлялись при изменении значения свойства источника.Пользовательские привязки JavaFX не работают

Мне удалось написать простой класс, чтобы воспроизвести эту проблему, и я действительно не понимаю, почему это происходит. Вот быстрый тест, который воспроизводит проблему:

import javafx.beans.binding.BooleanBinding; 
import javafx.beans.binding.ObjectBinding; 
import javafx.beans.property.BooleanProperty; 
import javafx.beans.property.ObjectProperty; 
import javafx.beans.property.SimpleBooleanProperty; 
import javafx.beans.property.SimpleObjectProperty; 
import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 

public class TestingBindingsFx { 

    private final ObjectProperty<MyEvent> objectProperty = new SimpleObjectProperty<MyEvent>(this, "objectProperty"); 
    private final BooleanProperty booleanProperty = new SimpleBooleanProperty(this, "booleanProperty"); 

    private ObjectBinding<MyEvent> bindingObj; 
    private BooleanBinding bindingBool; 

    public TestingBindingsFx(ObjectProperty<String> selection) { 
    setupBindings(selection); 
    } 

    private void setupBindings(ObjectProperty<String> selection) { 
    bindingObj = createObjectBinding(selection); 
    bindingBool = createBooleanBinding(selection); 
    objectProperty.bind(bindingObj); 
    booleanProperty.bind(bindingBool); 
    } 

    private static ObjectBinding<MyEvent> createObjectBinding(ObjectProperty<String> selection) { 
    return new ObjectBinding<MyEvent>() { 
     { 
     super.bind(selection); 
     } 

     @Override 
     protected MyEvent computeValue() { 
     System.out.println("createObjectBinding called"); 
     MyEvent ve = selection.get() == null ? MyEvent.EVENT1 
      : MyEvent.EVENT2; 
     return ve; 
     } 
    }; 
    } 

    private static BooleanBinding createBooleanBinding(ObjectProperty<String> selection) { 
    return new BooleanBinding() { 
     { 
     super.bind(selection); 
     } 

     @Override 
     protected boolean computeValue() { 
     System.out.println("createBooleanBinding called"); 
     return selection.get() == null ? true : false; 
     } 
    }; 
    } 

    public static void main(String[] args) { 
    ObjClass objclass = new ObjClass(); 
    System.out.println("Instantiating TestingBindingsFx..."); 
    TestingBindingsFx fx = new TestingBindingsFx(objclass.selection); 

    objclass.selection.addListener(new ChangeListener<String>() { 
     @Override 
     public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) { 
     System.out.println("changed " + oldValue + "->" + newValue); 
     } 
    }); 

    System.out.println("Changing selection property values..."); 
    objclass.selection.set("Test 1"); 
    objclass.selection.set("Test 2"); 
    } 

    enum MyEvent { 
    EVENT1, 
    EVENT2; 
    } 

    static class ObjClass { 
    public final ObjectProperty<String> selection = new SimpleObjectProperty<String>(this, "selection"); 
    } 
} 

Таким образом, после запуска этого я вижу:

Instantiating TestingBindingsFx... 
createObjectBinding called 
createBooleanBinding called 
Changing selection property values... 
changed null->Test 1 
changed Test 1->Test 2 

Когда я ожидал увидеть что-то вроде:

Instantiating TestingBindingsFx... 
createObjectBinding called 
createBooleanBinding called 
Changing selection property values... 
changed null->Test 1 
createObjectBinding called 
createBooleanBinding called 
changed Test 1->Test 2 
createObjectBinding called 
createBooleanBinding called 

ChangeListener работает как ожидалось (просто поставьте его там для целей проверки), и его вызывают каждый раз, когда я изменяю значение свойства выбора.

Но пользовательские привязки никогда не обновляются после первого раза и смотрят на код, который я не могу понять, почему. Сначала я думал, что это может быть связано со слабыми ссылками, но я даже превратил объекты привязки в переменные уровня класса и все же никаких изменений.

Я чувствую, что здесь может быть что-то важное, но после двух часов просмотра этого кода я просто не понимаю, почему. В моем реальном приложении это еще более странно, потому что одно из пользовательских привязок действительно работает нормально.

ответ

2

Это потому, что JavaFX ленив, серьезно.

Если зависимость недействительна, привязка считается недействительной, а InvalidationListener s уведомляются о потенциальном изменении. Если вы не добавите ChangeListener или воспользуетесь get для получения значения, метод computeValue никогда не используется, так как есть «никто, кто хочет узнать о новом значении». Это может повысить производительность.

Свойства связывания выполняются с использованием InvalidationListener, а свойства также обновляются лениво.

Вы можете, например. добавлять слушателей изменений к свойствам, чтобы заставить пересчет значений каждый раз, когда привязка недействительна:

private void setupBindings(ObjectProperty<String> selection) { 
    bindingObj = createObjectBinding(selection); 
    bindingBool = createBooleanBinding(selection); 
    objectProperty.bind(bindingObj); 
    booleanProperty.bind(bindingBool); 

    objectProperty.addListener((a,b,c)-> {}); 
    booleanProperty.addListener((a,b,c)-> {}); 
} 
+0

Бинго, вот и все! Я знал, что мне не хватает чего-то простого, я был настолько сосредоточен на том, что computeValue не называется, что полностью забыл о ленивом характере привязок. Мне не нужно принудительно пересчитывать, ленивость совершенно прекрасна. Мне просто не хватало, чтобы прочитать целевые свойства обратно на свой тестовый код после изменения значений свойств источника. Спасибо за быстрый ответ. – mfc