JavaFX: IntegerProperty.IntegerProperty() странное поведение

#properties #bind #javafx-8

#свойства #привязка #javafx-8

Вопрос:

На мой взгляд, у меня есть HBox

 @FXML
private HBox hboxWarning;
 

и я хочу скрыть / показать его в соответствии со значением

 private ObjectProperty<Integer> maxClientCount;
 

Если maxClientCount > 10 then hboxWarning виден, то он скрывается.
Я связал два элемента таким образом

 hboxWarning.visibleProperty().bind(IntegerProperty.integerProperty(maxClientCount).greaterThan(10));
 

и работает хорошо. Моя проблема в том, что

 IntegerProperty.integerProperty(maxClientCount)
 

обнуляет текущее значение maxClientCount . Это ошибка JavaFX или я использую IntegerProperty.integerProperty неправильно? И
как я могу достичь своей цели?

Комментарии:

1. это ошибка: javafx-jira.kenai.com/browse/RT-37523

Ответ №1:

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

 // method in u5, binds the wrong way round 
// (for usage in IntegerProperty.integerProperty) 
public static BidirectionalBinding bindNumber(Property<Integer> property1, 
       IntegerProperty property2) 

// calls 
private static <T extends Number> BidirectionalBinding bindNumber(Property<T> property1, 
       Property<Number> property2) {
 

Последовательность имеет решающее значение, потому что нам нужно приведение типа от Number к T при установке значения p1 (что безопасно, потому что мы знаем, что свойство number-type справляется с преобразованием из Number -> concrete type). Основное исправление просто добавляет все эти методы с переключаемой последовательностью параметров.

Для пользовательского взлома до выпуска JDK 8u20 единственный способ, который я вижу, — это использовать не специальные методы привязки номеров, а привязку к общему объекту:

 public static IntegerProperty integerProperty(final Property<Integer> property) {
    if (property == null) {
        throw new NullPointerException("Property cannot be null");
    }
    return new IntegerPropertyBase() {
        {
            bindBidirectional(cast(property));
            // original:
            //BidirectionalBinding.bindNumber(property, this);
        }

        @Override
        public Object getBean() {
            return null; // Virtual property, no bean
        }

        @Override
        public String getName() {
            return property.getName();
        }

        @Override
        protected void finalize() throws Throwable {
            try {
                unbindBidirectional(cast(property));
                // original
                // BidirectionalBinding.unbindNumber(property, this);
            } finally {
                super.finalize();
            }
        }
    };
}

/**
 * Type cast to allow bidi binding with a concrete XXProperty (with
 * XX = Integer, Double ...). This is (?) safe because the XXProperty
 * internally copes with type conversions from Number to the concrete
 * type on setting its own value and exports the concrete type as
 * needed by the object property.
 * 
 */
private static <T extends Number> Property<Number> cast(Property<T> p) {
    return (Property<Number>) p;
}
 

Отнеситесь к этому с недоверием — при элементарном тестировании могут быть побочные эффекты, которые я пропустил.

Комментарии:

1. Спасибо @kleopatra. Я не очень хорошо понимаю, почему непроверенное приведение можно считать безопасным 🙂

2. нас интересует только значение, которое синхронизируется в привязке, выполняемой через setValue / GetValue . Тогда T должен быть типом конкретного числового типа, то есть IntegerProperty <-> ObjectProperty<Integer> или DoubleProperty <-> ObjectProperty<Double>, чтобы быть безопасным для другого

3. Ваш обходной путь работает!! Почему компилятор выдает мне «непроверенное» предупреждение в методе приведения? Число является суперклассом T… :/

Ответ №2:

Как правильно сказал @kleopatra, это ошибка JavaFX, исправленная в JDK 8u20.

Тем временем я использовал следующий обходной путь:

 int maxClients = maxClientCount.get();
hboxWarning.visibleProperty().bind(IntegerProperty.integerProperty(maxClientCount).greaterThan(10));
maxClientCount.setValue(maxClients);
 

Я надеюсь, что это может кому-то помочь.

Комментарии:

1. я бы предпочел вообще не использовать фабрику — c amp; p, исправьте адаптер и используйте его до тех пор, пока не будет выпущено исправление. Разрешение установить поврежденное свойство, а затем отменить его, может вызвать проблемы с а) потенциальными слушателями (они получат неожиданные уведомления) б) самим адаптированным свойством (набор адаптера может нарушать внутренние правила проверки) Впрочем, как и во всех обходных путях ошибок, это дело вкуса 🙂

2. Я согласен с вами, что мой обходной путь может создать проблемы. Я бы последовал вашему совету, но я не понимаю, что я должен скопировать и вставить, а затем изменить? Класс IntegerProperty?

3. IntegerProperty.IntegerProperty — это статический метод, который внутренне обертывает свойство IntegerProperty вокруг вашего свойства<Integer> — c amp; p этот метод и инвертирует все привязки (свойство, this) к bind(this, property)

4. ах .. теперь я понимаю, почему ошибка произошла в первую очередь (только обнаружив ее, я никогда не пытался исправить ее сам) — общий ввод текста стоит на пути. Посмотрит на исправление в b20, а затем вернется