#java #scala #javafx
#java #scala #javafx
Вопрос:
У меня есть реализация I18N, которая связывает элементы пользовательского интерфейса JavaFX с помощью свойств, например:
def translateLabel(l: Label, key: String, args: Any*): Unit =
l.textProperty().bind(createStringBinding(key, args))
Привязка свойств проста и хорошо работает. Однако я борюсь с ComboBox, поскольку он принимает ObservableList (строк в моем случае), и я понятия не имею, как привязать к этому мои функции переводчика. Я не согласен с различием между ObservableValue
ObservableList
и Property
интерфейсами, поскольку все они звучат одинаково.
У него есть itemsProperty()
, и valueProperty()
, однако, документация для них отсутствует и расплывчата, поэтому я не уверен, где их можно использовать.
Что я хочу сделать, так это иметь поле со списком, в котором все элементы (или, по крайней мере, выбранный / видимый) динамически изменяют язык (I18N), как если бы он был привязан, точно так же, как свойство.
Редактировать:
Просто чтобы упростить понимание, моя текущая реализация:
private def setAggregatorComboBox(a: Any): Unit = {
val items: ObservableList[String] = FXCollections.observableArrayList(
noneOptionText.getValue,
"COUNT()",
"AVG()",
"SUM()"
)
measureAggregatorComboBox.getItems.clear()
measureAggregatorComboBox.getItems.addAll(items)
}
Где noneOptionText
a StringProperty
, который уже привязан к a StringBinding
, который преобразуется при создании экземпляра класса таким образом:
def translateString(sp: StringProperty, key: String, args: Any*): Unit =
sp.bind(createStringBinding(key, args))
Комментарии:
1.
items
не будет работать так, как вы хотите;noneOptionText.getValue
будет оцениваться приitems
создании; значение первого элемента не изменится при изменении значенияnoneOptionText
.
Ответ №1:
Это itemsProperty()
список элементов, отображаемых во всплывающем окне со списком; его значение равно an ObservableList
.
Это valueProperty()
выбранный элемент (или значение, введенное пользователем, если поле со списком доступно для редактирования).
Что я бы порекомендовал, так это сделать так, чтобы данные в поле со списком были списком ключей, и использовать пользовательские ячейки для привязки текста в каждой ячейке к переводу этих ключей. Я не говорю на scala, но на Java это выглядит так:
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().setAll(getAllKeys());
class TranslationCell extends ListCell<String> {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
textProperty().unbind();
if (empty || item == null) {
setText("");
} else {
textProperty().bind(createStringBinding(item));
}
}
}
comboBox.setCellFactory(lv -> new TranslationCell());
comboBox.setButtonCell(new TranslationCell());
Теперь обратите внимание, что valueProperty()
содержит ключ для выбранного значения.
Если вы действительно хотите привязать элементы к an ObservableValue<ObservableList<String>>
, вы можете сделать что-то вроде:
comboBox.itemsProperty().bind(Bindings.createObjectBinding(() ->
FXCollections.observableArrayList(...),
...));
где первое ...
— это varargs String
значений, а второе ...
— наблюдаемое значение, изменения в котором потребуют пересчета списка. (Итак, в вашем случае, я предполагаю, что у вас есть ObservableValue<Locale>
где-то, представляющее текущую локаль; вы бы использовали это для второго аргумента.)
В вашем конкретном случае использования (где интернационализируется только первый элемент списка) может быть проще просто использовать прослушиватель:
comboBox.getItems().setAll(
noneOptionTest.getValue(),
"COUNT()",
"AVG()",
"SUM");
noneOptionTest.addListener((obs, oldVal, newVal) ->
comboBox.getItems().set(0, newVal));
хотя я согласен, что это немного менее элегантно.
Для полноты:
Я не согласен с различием между
ObservableValue
ObservableList
иProperty
интерфейсами, поскольку все они звучат одинаково.
ObservableValue<T>
: представляет единственное значение типа T
, которое можно наблюдать (это означает, что код может быть выполнен при его изменении).
Property<T>
: представляет собой доступный для записи ObservableValue<T>
; предполагается, что реализации будут иметь фактическую переменную, представляющую значение. Он определяет дополнительные функциональные возможности, позволяющие привязывать его значение к другим ObservableValue<T>
.
Так, например:
DoubleProperty x = new SimpleDoubleProperty(6);
DoubleProperty y = new SimpleDoubleProperty(9);
ObservableValue<Number> product = x.multiply(y);
x
и y
то и Property<Number>
другое; реализация SimpleDoubleProperty
имеет фактическую double
переменную, представляющую это значение, и вы можете делать что-то вроде y.set(7);
изменения значения.
С другой стороны, product
это не a Property<Number>
; вы не можете изменить его значение (потому что это нарушило бы привязку: объявленный инвариант, который product.getValue() == x.getValue() * y.getValue()
); однако он заметен, поэтому вы можете привязаться к нему:
BooleanProperty answerCorrect = new SimpleBooleanProperty();
answerCorrect.bind(product.isEqualTo(42));
и т.д.
An ObservableList
несколько отличается: это a java.util.List
(набор элементов), и вы можете наблюдать, как он реагирует на операции в списке. Т.Е. Если вы добавляете слушателя в an ObservableList
, слушатель может определить, были ли добавлены или удалены элементы и т.д.
Комментарии:
1. значение немного сложнее, если поле со списком доступно для редактирования .. что вы имеете в виду? Это всегда (за исключением случаев, когда значение привязано) выбранный элемент, не зависящий от возможности редактирования
2. @kleopatra Если он доступен для редактирования,
value
он может не быть «элементом» в смысле элементаgetItems()
; т. Е. Это может быть не что-то выбранное из списка элементов. (Мы могли бы поспорить о семантике того, что подразумевается под «выбранным элементом»; это то, что набрано в «выбранном» и т. Д. Но …) Отредактировано для ясности.3. тем не менее, SelectedItem (в терминах selectionModel) — это значение, которое может содержаться в элементах или нет :). Инвариант:
assertEquals(box.getValue(), box.getSelectionModel().getSelectedItem()
должен сохраняться всегда и не должен зависеть от возможности редактирования (есть некоторые ошибки, нарушающие его 😉4. @kleopatra Это правда? В документах говорится: «Очистка выделения в модели выбора не обнуляет свойство valueProperty; оно остается таким же, как и раньше». Итак, после очистки модели выбора, либо
getSelectionModel().getSelectedItem() != getValue()
илиgetSelectionModel().getSelectedItem() != null amp;amp; getSelectionModel().getSelectedIndices().size() == 0
, что кажется еще более странным…5. Однако @
ObjectProperty<ObservableList<T>>
Shobe является реализациейObservableValue<ObservableList<T>>
, поэтому также верно, чтоitemsProperty()
возвращает anObservableValue<ObservableList<T>>
. И то, что я на самом деле заявил, касалось привязки : любойProperty<S>
может быть привязан к любомуObservableValue<S>
. SoitemsProperty().bind(...)
может принимать любойObservableValue<ObservableList<T>>
(который возвращаетсяBindings.createObjectBinding(...)
кодом, который я использовал). (Я согласен, что API несколько перегружен.)