JavaFX Свойство
Конвенции для определения name
собственности в вашем ViewModel
класса:
public class ViewModel {
private StringProperty name;
public ViewModel() {
// parameters are owning bean, property name, and initial value,
// and are optional for the property convention
name = new SimpleStringProperty(this, "name", "");
}
public final String getName() {
return name.get();
}
public final void setName(String name) {
this.name.set(name);
}
// Note this method name:
public final StringProperty nameProperty() {
return name;
}
}
Есть несколько вариантов: в частности, если вы хотите, чтобы сделать вещи переопределения, вы можете удалить модификатор final
от nameProperty()
и заменить this.name
на this.nameProperty()
в get и se t методов. Ключевым моментом является то, что вызов setName(...)
всегда дает тот же результат, что и nameProperty().set(...)
(и аналогично для методов get).
Я думаю, что ваша общая интерпретация как-то отключена на один уровень (если это имеет смысл). Класс StringProperty
определяет методы get
и set
, и это методы, которые будут автоматически вызываться для вас из-за вашей привязки. Поэтому (даже если у вас есть несколько нестандартное соглашение об именах), ввод текста в текстовое поле будет по-прежнему обновлять значение свойства. API привязки не использует (много?) Отражение, насколько мне известно - он просто регистрирует слушателей со свойствами и обновляет их, когда другой изменяется.
Что касается пользовательских геттеров и сеттеров, то они действительно поддерживаются только для классов свойств с помощью описанной выше техники. Тем не менее, я никогда не нашел для них хороший вариант использования, особенно с API привязок, который дает вам возможность легко создавать зависимые значения.
Update
Таким образом, для вашего конкретного примера, я бы реализовать по следующим направлениям:
public class ViewModel {
private StringProperty name = new SimpleStringProperty(this, "name");
// usual JavaFX Property methods...
}
Тогда там, где вам это нужно:
Predicate<String> predicate = ... ;
BooleanBinding canExecute = Bindings.createBooleanBinding(() ->
predicate.test(viewModel.getName()),
viewModel.nameProperty());
, а затем вы может делать такие вещи, как
Button nextButton = new Button("Next");
nextButton.disableProperty().bind(canExecute.not());
Если предикат может измениться, вы можете даже сделать
ObjectProperty<Predicate<String>> predicate = new SimpleObjectProperty<>(s -> true);
BooleanBinding canExecute = Bindings.createBooleanBinding(() ->
predicate.get().test(viewModel.getName()),
predicate, viewModel.nameProperty());
Есть много, много фабричных методов в Bindings
class для создания привязок, и вы также можете создать подкласс абстрактных ObjectBinding
, StringBinding
и т.д. классов, если это необходимы ,
Обратите внимание, что существует тонкое изменение в философии между этим подходом и предложением, предложенным в вопросе: в подходе в вопросе (подклассификация SimpleStringProperty
) логика определения, является ли действие исполняемым, удерживается свойством string. В этом подходе он учитывается в другом объекте, который наблюдает за строковым свойством (через привязку, которая фактически регистрирует WeakInvalidationListener
на свойстве строки).
Уведомление
Вы можете зарегистрировать InvalidationListener
s или ChangeListener
s со свойством. Метод addListener(InvalidationListener)
наследуется от Observable
и указывает, что последнее значение, наблюдаемое в данный момент, может быть недействительным. Это позволяет «ленивые оценки» наблюдаемых, которые только вычисляют новое значение, когда оно запрашивается. Метод addListener(ChangeListener)
наследуется от ObservableValue
, который (как указывает его имя) является наблюдаемым, который явно переносит значение. Регистрация ChangeListener
требует постоянной оценки, так как слушатель уведомляется о новом значении. API слишком тонкий для моего вкуса, хотя он обеспечивает много и много гибкости для высокопроизводительных реализаций.
Так в тестовом приложении, вы можете сделать
viewModel.nameProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> obs, String oldValue, String newValue) {
System.out.println("Name changed from "+oldValue+" to "+newValue);
}
});
или, в Java 8, гораздо более емким
viewModel.nameProperty().addListener((obs, oldName, newName) ->
System.out.println("Name changed from "+oldValue+" to "+newValue));
Разница между недостоверности слушателей и изменить слушателей только становится очевидным, если вы имеют ObservableValue
, что зависит от вычисления. Сравните:
IntegerProperty x = new SimpleIntegerProperty(2);
IntegerProperty y = new SimpleIntegerProperty(3);
ObservableNumberValue sum = x.add(y);
sum.addListener(obs -> System.out.println("Invalidated")); // invalidation listener
x.set(3);
y.set(5);
с тем же кодом, но со слушателем изменения:
sum.addListener((obs, oldSum, newSum) -> System.out.println("Changed"));
вместо недействительности слушателя.
В tutorial есть более подробная информация.
Обновленный ответ в ответ на редактирование. –