2016-11-08 2 views
1

У меня проблема с моим InvalidationListener. Он установлен как Listener для SimpleStringProperty. Но он предназначен только для первого изменения SimpleStringProperty. Я вошел в режим отладки и сделал точку прерывания на строке, которая вызывает SimpleStringProperty :: set, и она начала работать, пока я не удалил точку останова.InvalidationListener выполняется только в режиме отладки с точкой останова

Я сделал короткую исполняемую программу примера, которая имитирует модификацию SimpleStringProperty с помощью таймера. Вы можете запустить программу один раз без точек разрыва и один раз, имеющей точку останова на этой линии: property.set(value);

import javafx.animation.KeyFrame; 
import javafx.animation.Timeline; 
import javafx.application.Application; 
import javafx.beans.property.SimpleStringProperty; 
import javafx.scene.Scene; 
import javafx.scene.layout.BorderPane; 
import javafx.stage.Stage; 
import javafx.util.Duration; 


public class Main extends Application { 

    private SimpleStringProperty property; 
    private int counter; 

    @Override 
    public void start(Stage stage) { 
     // open a window to avoid immediately termination 
     stage.setWidth(800); 
     stage.setHeight(600); 
     BorderPane pane = new BorderPane(); 
     stage.setScene(new Scene(pane)); 
     stage.show(); 

     // create a SimpleObjectProperty 
     property = new SimpleStringProperty(); 
     property.addListener(observable -> 
      System.out.println("New value is: " + counter) 
     ); 
     counter = 0; 

     // create timer to change 'property' every second 
     Timeline timeline = new Timeline(); 
     KeyFrame keyFrame = new KeyFrame(Duration.seconds(2), event ->{ 
      String value = "" + ++counter; 
      System.out.println("Set property to: " + value); 
      property.set(value); 
     }); 
     timeline.getKeyFrames().add(keyFrame); 
     timeline.setCycleCount(Timeline.INDEFINITE); 
     timeline.playFromStart(); 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
} 

Выход на моей машине (Linux Mint 16.04 64bit, Oracle-Java 1.8.0_111):

Set property to: 1 
New value is: 1 
Set property to: 2 
Set property to: 3 
Set property to: 4 
... 

Пожалуйста, объясните мне:

  1. Почему слушатель не вызывается при каждом изменении?
  2. Почему прослушиватель называется, когда я устанавливаю точку прерывания?
  3. Что делать, чтобы заставить его работать без перерывов?
+0

Пожалуйста, откройте новый вопрос для этих вещей. – Zerlono

ответ

5

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

В общем, значение наблюдаемого значения может быть чем-то, что вычисляется, а не просто хранится в поле. После того, как значение было «реализовано» (моя терминология), путем вычисления, если оно вычисляется, или путем извлечения, если оно просто хранится в поле, тогда наблюдаемое значение находится в «правильном» состоянии. Если значение изменяется (или может измениться), тогда наблюдаемое значение становится «недействительным», указывая, что его, возможно, придется пересчитать или снова просмотреть.

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

property.set(value); 

переходы собственности в недопустимом состоянии (потому что совсем недавно полученное значение, если таковые имеются, не его текущее значение).

Поскольку вы никогда не звоните property.get() (или property.getValue()), свойство никогда не проверяется. Следовательно, в следующий раз, когда вы вызываете property.set(value), свойство делает не переход в неправильное состояние (он уже находится в этом состоянии), и поэтому слушатель не уволен.

Если вы замените слушатель коды с

property.addListener(observable -> 
    System.out.println("New value is: " + property.get()) 
); 

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

Настоящая проблема заключается в том, что вы используете неправильный вид слушателя здесь.Если вы хотите, чтобы выполнить действие каждый раз, когда значение изменяется используйте ChangeListener, а не InvalidationListener:

property.addListener((observable, oldValue, newValue) -> 
    System.out.println("New value is: " + newValue) 
); 

наблюдения, что работает в режиме отладки с точкой останова приводит к недействительности слушателю, чтобы вызываться каждый раз, когда это интересный. Я немного догадываюсь, но то, что я подозреваю, заключается в том, что когда вы нажимаете точку останова, отладчик показывает текущие значения переменных. Это неизбежно включает в себя вызов getValue() об этом свойстве (как часть его реализации toString()), и поэтому свойство будет проверено.

Вы вряд ли часто используете InvalidationListener. Их основное использование - в привязках. Рассмотрим следующий пример:

DoubleProperty x = new SimpleDoubleProperty(3); 
DoubleProperty y = new SimpleDoubleProperty(4); 

DoubleBinding hyp = new DoubleBinding() { 
    { 
     bind(x); 
     bind(y); 
    } 

    @Override 
    protected double computeValue() { 
     System.out.println("Computing distance"); 
     return Math.sqrt(x.get()*x.get() + y.get()*y.get()); 
    } 
}; 

Label hypLabel = new Label(); 
hypLabel.textProperty().bind(hyp.asString("Hypotenuse: %f")); 

Вызов bind(x) в связывающем реализации означает: когда x становится недействительным, считают это обязательным недействительным. Аналогично для y. Конечно, реализация bind использует InvalidationListener с под капотом.

Дело в том, что вычисление стоимости hyp довольно дорого. Если вы должны были внести несколько изменений в x или y, то hyp станет недействительным и нуждается в перерасчете , если вам это нужно еще раз. Поскольку свойство text метки привязано к hyp, это также означает, что текст ярлыка становится недействительным. Однако вам действительно нужно новое значение, когда ярлык перерисовывается в импульсе рендеринга; было бы излишним вычислить значение для каждого изменения x и y.

+0

Ваши права. InvalidationListener неправильно используется в примере кода, но не в моей исходной программе. Но, как вы полагаете, я вместо этого меняю его как ChangeListener. Спасибо за подробное объяснение. – Zerlono

+1

@ Zerlono Вам редко требуется 'InvalidationListener'. Обновление ответа, чтобы показать его основную цель ... –

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