У меня есть один пользовательский вид с другим. Иерархия:
MyOuterView
->MyInnerView
MyInnerView
имеет атрибут enum, например:
<attr name="myAttr" format="enum">
<enum name="foo" value="0"/>
<enum name="bar" value="1"/>
</attr>
Поэтому я могу создать экземпляр компонента в MyOuterView
XML, например:
<com.example.MyInnerView
....
app:myAttr="foo"/>
Что, конечно, работает. MyOuterView
предлагает аргумент для самой настройки. Основываясь на этом аргументе, я хочу установить аргумент MyInnerView
.
Желаемое поведение заключается в том, что я могу работать с Data Binding, например:
<com.example.MyInnerView
....
app:myAttr="@{data.getMyAttr()}"/>
где getMyAttr()
выглядит так:
public int getMyAttr() {
return myAttr; // returns 0 or 1
}
Результатом является компиляция.
****/ошибка привязки данных **** msg: Не удается найти установщик для атрибута 'app: myAttr' с типом параметра int на com.example.MyInnerView
Поэтому, очевидно, я не могу установить перечисление по значению, но только по имени. Любая идея, помимо создания MyInnerView
программно? Обратите внимание, что я не могу изменить MyInnerView
.
Ответ 1
Я не могу установить перечисление по значению, но только по имени
Это не совсем верно. Вы не сможете установить значение с помощью DataBinding даже по имени, дело в том, что значение атрибута, определенного в xml, передается в View
через конструктор. В View
может быть как атрибут, так и сеттер, но это необязательно. Например, вы задали значение для android:text
TextView
, для этого значения установлено значение com.android.internal.R.styleable.TextView_text
который затем извлекается из AttributeSet
в конструкторе. Если вы используете DataBinding, вызывается setText()
, но это две совершенно разные вещи, эта функция одна и та же, но они не связаны в коде.
Учитывая, что вы не можете изменять MyInnerView
и что нет setter, вы можете вызвать myAttr
, ваш единственный вариант - передать его через конструктор. DataBinding просто не представляется возможным, даже с BinderAdapter вы не смогли бы установить значение в AttributeSet
, поскольку View
уже инициализируется в этой точке.
Вариант 1 - темы
Определить новый атрибут
<attr name="MyInnerViewAttrValue" format="integer" />
Затем разрешите этот атрибут со стилем, например, вы могли бы
<style name="AppTheme.Foo">
<item name="MyInnerViewAttrValue">0</item>
</style>
<style name="AppTheme.Bar">
<item name="MyInnerViewAttrValue">1</item>
</style>
Установите его в макет xml
<com.example.MyInnerView
...
app:myAttr="?attr/MyInnerViewAttrValue" />
А затем вызовите setTheme(int)
в Activity
перед созданием представлений.
Вариант 2 - пользовательский BindingAdapter
Если вы хотите установить значение DataBinding (потому что у вас был сеттер или потому, что вы хотели взломать MyInnerView
с отражением), вам нужно будет создать собственный BindingAdapter, например
@BindingAdapter("myAttrValue")
public static void setMyAttr(MyInnerView myInnerView, int value) {
switch (value) {
case 0:
myInnerView.foo();
break;
default:
myInnerView.bar();
}
}
Затем в макете xml
<com.example.lelloman.dummy.MyInnerView
...
myAttrValue="@{model.attr}" />
И дайте значение int
из вашей ViewModel
public class MyInnerViewModel {
public int getAttr() {
return 1234;
}
}
Ответ 2
К сожалению, вы можете использовать имя атрибута, например app:myAttr="@{...}"
только если для этого есть установщик, и этот сеттер сопоставляется с именем атрибута или может быть разрешен его именем. В вашем случае, если этот атрибут используется только в конструкторе представлений, а в представлении нет общедоступного метода для изменения - у вас все еще есть несколько возможных решений: - расширить представление и реализовать логику для настройки вида, как этот атрибут (если возможно) - сделайте BindingAdapter и используйте Reflection (если возможно). - создать свой собственный вид путем копирования и вставки)