Динамические значения атрибутов в FXML? - PullRequest
0 голосов
/ 05 мая 2019

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

Будет исключение ArrayIndexOutOfBoundsException, поскольку оно не может удалить элемент человека с индексом -1. Индекс -1 был возвращен getSelectedIndex () - это означает, что выбора не было.

Игнорировать такую ​​ошибку, конечно, не очень приятно. Мы должны позволить Пользователь знает, что он / она должен выбрать человека перед удалением. (Четное лучше было бы, если бы мы отключили кнопку, чтобы пользователь не даже есть шанс сделать что-то не так.)

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

Конечно, мы можем сделать это следующим образом:

peopleTable.getSelectionModel().selectedItemProperty().addListener(
  (observable, oldValue, newValue) -> {

    showPersonDetails(newValue);

    boolean somebodySelected = peopleTable.getSelectionModel().getSelectedIndex() >= 0;
    button.setDisable(!somebodySelected);
  }
);

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

<Button
  mnemonicParsing="false"
  onAction="#handleDeletePerson"
  text="Delete"
  disable="disableDeleteButtonFlag"
/>

Если возможно динамическое значение атрибута, больше нет необходимости явно вызывать button.setDisable(). Однако приведенный ниже код не работает.

@FXML
private boolean disableDeleteButtonFlag = true;

// ...


@FXML
private void initialize() {


  // ...

  peopleTable.getSelectionModel().selectedItemProperty().addListener(
      (observable, oldValue, newValue) -> {
        showPersonDetails(newValue);
        disableDeleteButtonFlag = peopleTable.getSelectionModel().getSelectedIndex() < 0;
      }
  );
}

1 Ответ

2 голосов
/ 05 мая 2019

Во-первых, я не уверен, что ссылка на поле boolean позволит даже загрузить файл FXML (точно не указано, как он "не работает"). Но игнорируя это, поля не наблюдаются в Java, что означает, что обновление поля не приведет к автоматическому обновлению свойства disable Button. Вот почему JavaFX имеет интерфейсы [ReadOnly]Property и вводит " JavaFX Bean " (расширение Java Bean , которое добавляет "средство получения свойства"); это позволяет наблюдателям видеть изменения в свойствах объекта. Обратите внимание, что вы можете связать записываемое свойство с ObservableValue, чтобы оно всегда имело одно и то же значение.

Теперь я ожидал бы, что привязка выражения будет тем, что вы ищете, но следующее:

<ListView fx:id="list"/>
<Button disable="${list.selectionModel.selectedIndex == -1}"/>

Кажется, не работает - я получаю исключение (используя JavaFX 12.0.1):

Caused by: java.lang.ClassCastException: class java.lang.Long cannot be cast to class java.lang.Integer (java.lang.Long and java.lang.Integer are in module java.base of loader 'bootstrap')
    at java.base/java.lang.Integer.compareTo(Integer.java:64)
    at javafx.fxml/com.sun.javafx.fxml.expression.Expression.lambda$equalTo$5(Expression.java:1105)
    at javafx.fxml/com.sun.javafx.fxml.expression.BinaryExpression.evaluate(BinaryExpression.java:55)
    at javafx.fxml/com.sun.javafx.fxml.expression.ExpressionValue.getValue(ExpressionValue.java:192)
    at javafx.base/com.sun.javafx.binding.ExpressionHelper.addListener(ExpressionHelper.java:53)
    at javafx.base/javafx.beans.value.ObservableValueBase.addListener(ObservableValueBase.java:55)
    at javafx.fxml/com.sun.javafx.fxml.expression.ExpressionValue.addListener(ExpressionValue.java:201)
    at javafx.base/javafx.beans.binding.BooleanBinding.bind(BooleanBinding.java:106)
    at javafx.base/javafx.beans.property.BooleanPropertyBase$ValueWrapper.<init>(BooleanPropertyBase.java:254)
    at javafx.base/javafx.beans.property.BooleanPropertyBase.bind(BooleanPropertyBase.java:168)
    at javafx.fxml/javafx.fxml.FXMLLoader$Element.processPropertyAttribute(FXMLLoader.java:326)
    at javafx.fxml/javafx.fxml.FXMLLoader$Element.processInstancePropertyAttributes(FXMLLoader.java:242)
    at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processEndElement(FXMLLoader.java:775)
    at javafx.fxml/javafx.fxml.FXMLLoader.processEndElement(FXMLLoader.java:2838)
    at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2557)
    ... 17 more

Вместо этого используется свойство selectedItem:

<Button disable="${list.selectionModel.selectedItem == null}"/>

Дает другое исключение:

Caused by: java.lang.NullPointerException
    at javafx.fxml/com.sun.javafx.fxml.expression.Expression.lambda$equalTo$5(Expression.java:1105)
    at javafx.fxml/com.sun.javafx.fxml.expression.BinaryExpression.evaluate(BinaryExpression.java:55)
    at javafx.fxml/com.sun.javafx.fxml.expression.ExpressionValue.getValue(ExpressionValue.java:192)
    at javafx.base/com.sun.javafx.binding.ExpressionHelper.addListener(ExpressionHelper.java:53)
    at javafx.base/javafx.beans.value.ObservableValueBase.addListener(ObservableValueBase.java:55)
    at javafx.fxml/com.sun.javafx.fxml.expression.ExpressionValue.addListener(ExpressionValue.java:201)
    at javafx.base/javafx.beans.binding.BooleanBinding.bind(BooleanBinding.java:106)
    at javafx.base/javafx.beans.property.BooleanPropertyBase$ValueWrapper.<init>(BooleanPropertyBase.java:254)
    at javafx.base/javafx.beans.property.BooleanPropertyBase.bind(BooleanPropertyBase.java:168)
    at javafx.fxml/javafx.fxml.FXMLLoader$Element.processPropertyAttribute(FXMLLoader.java:326)
    at javafx.fxml/javafx.fxml.FXMLLoader$Element.processInstancePropertyAttributes(FXMLLoader.java:242)
    at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processEndElement(FXMLLoader.java:775)
    at javafx.fxml/javafx.fxml.FXMLLoader.processEndElement(FXMLLoader.java:2838)
    at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2557)
    ... 17 more

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

<VBox xmlns="http://javafx.com/" xmlns:fx="http://javafx.com/fxml" fx:controller="Controller">
    <ListView fx:id="list"/>
    <Button fx:id="button"/>
</VBox>
public class Controller {

    @FXML private ListView<YourType> list;
    @FXML private Button button;

    @FXML
    private void initialize() {
        button.disableProperty()
                .bind(list.getSelectionModel().selectedIndexProperty().isEqualTo(-1));
    }

}

Метод isEqualTo вернет BooleanBinding со значением true всякий раз, когда свойство selectedIndex содержит -1. Свойство disable затем связывается с этим BooleanBinding, поэтому оно всегда имеет одно и то же значение. Вы можете прочитать Использование JavaFX Properties и Binding для получения дополнительной информации.

...