Где я должен проверить изменения свойств JavaFX?

У меня есть mvp-структурированное приложение javafx. Существует представление с текстовым полем, которое имеет свой собственный textProperty типа StringProperty. Существует также модель, которая содержит объект, называемый Item. Элемент имеет IntegerProperty.

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

Bindings.bindBidirectional( textField.textProperty(), item.percentProperty(), new NumberStringConverter() );

Это работает отлично, если только значение текстового поля не будет очищено, что приведет к NullPointerException, потому что пустое значение textProperty приводит к нулевому значению и установка нулевого значения в IntegerProperty приводит к a NullPointerException. Можете ли вы придумать любой способ избежать этого? Должен ли я написать свой собственный NumberStringConverter?

Кроме того, я хотел бы определить, что Item может содержать только процентное значение от 0 до 100. При значении недопустимое значение следует информировать, поэтому пользователь может получить обратную связь. Где я должен проверять эти бизнес-единицы?

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

class PercentProperty extends SimpleIntegerProperty
{
  private InvalidValueListener invalidValueListener = null;

  public PercentProperty ( final Integer defaultValue )
  {
    set( defaultValue );
  }

  @Override
  public void set( final int newValue )
  {
    if ( isValid( newValue ) )
    {
      super.set( newValue );
      if ( invalidValueListener != null )
        invalidValueListener.validValue();
    }
    else
    {
      if ( invalidValueListener != null )
        invalidValueListener.invalidValue();
    }
  }

  private boolean isValid( final int value )
  {
    return (value >= 0 && value <= 100);//FIXME: Better use Predicates to define Rules.
  }

  public void setListener( final InvalidValueListener listener )
  {
    invalidValueListener = listener;
  }

  public void removeListener( @SuppressWarnings( "unused" ) final InvalidValueListener listener )
  {
    invalidValueListener = null;
  }

  protected void fireInvalidationValue()
  {
    invalidValueListener.invalidValue();
  }
}


interface InvalidValueListener
{
  void validValue();
  void invalidValue();
}

Ответ 1

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

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

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

Как побочная заметка, я бы не использовал два свойства в целом, и я бы пересмотрел ваши три уровня. MVP и все производные MVC оказались хорошими шаблонами для создания графических инструментов GUI, но я никогда не был уверен, что они одинаково хороши для структурирования графических приложений. Я имею в виду, если то, что вы называете моделью, - это способ обмена данными сеанса между различными частями приложения (вид приемника событий), то это совершенно законная реализация, иначе я не вижу смысла иметь отдельный набор свойств, сгруппированных в класс. В последнем случае сами виджеты являются моделью:

// This is the controller
public class PesonalDetails {
  // Model starts here: it implicitely defined by the widgets
  // You may also use @FXML
  private final TextField first = new TextField();
  private final TextField last = new TextField();
  // Model ends here
} 

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

  • Уровень визуального макета (построитель компоновки, реализованный на Java или FXML)
  • Код обработки событий
  • Если применимо, уровень доступа к данным (и вы можете применить шаблон здесь, например, ActiveRecord)

Ответ 2

(новая версия ответа)

Я думаю, что лучший aproach - не позволить пользователю вводить неправильное значение в первую очередь. Вы можете легко достичь этого с помощью JideFX Fields:

FormattedTextField<Integer> field = new FormattedTextField<>();
field.getPatternVerifiers().put("p", new IntegerRangePatternVerifier(0, 100));
field.setPattern("p");
field.valueProperty().bindBidirectional(item.percentProperty());

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

Ссылки:

Руководство для разработчиков JideFX Fields: http://www.jidesoft.com/jidefx/JideFX_Fields_Developer_Guide.pdf

Исходный код: https://github.com/jidesoft/jidefx-oss

Двоичный: http://search.maven.org/#search%7Cga%7C1%7Cjidefx