Привязка данных Android к пользовательскому представлению
Руководство по привязке данных Android обсуждает значения привязки в рамках действия или фрагмента, но есть ли способ выполнить привязку данных к пользовательскому виду?
Я хотел бы сделать что-то вроде:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mypath.MyCustomView
android:id="@+id/my_view"
android:layout_width="match_parent"
android:layout_height="40dp"/>
</LinearLayout>
с my_custom_view.xml
:
<layout>
<data>
<variable
name="myViewModel"
type="com.mypath.MyViewModelObject" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{myViewModel.myText}" />
</LinearLayout>
</layout>
Пока это возможно сделать, установив пользовательские атрибуты в пользовательском представлении, это быстро станет громоздким, если будет много значений для привязки.
Есть ли хороший способ выполнить то, что я пытаюсь сделать?
Ответы
Ответ 1
В вашем пользовательском представлении раздуйте макет, но вы, как правило, хотите, и установите установщик для атрибута, который вы хотите установить:
private MyCustomViewBinding mBinding;
public MyCustomView(...) {
...
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mBinding = MyCustomViewBinding.inflate(inflater);
}
public void setMyViewModel(MyViewModelObject obj) {
mBinding.setMyViewModel(obj);
}
Затем в макете вы используете ее в:
<layout xmlns...>
<data>
<variable
name="myViewModel"
type="com.mypath.MyViewModelObject" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mypath.MyCustomView
android:id="@+id/my_view"
app:myViewModel="@{myViewModel}"
android:layout_width="match_parent"
android:layout_height="40dp"/>
</LinearLayout>
</layout>
В приведенном выше атрибуте автоматической привязки создан для приложения: myViewModel, потому что есть сеттер с именем setMyViewModel.
Ответ 2
Во-первых, не делайте этого, если этот пользовательский вид уже является <include>
в другом макете, таком как активность и т.д. Вы просто получите исключение из-за неожиданного значения тега. Связывание данных уже выполнило привязку на нем, поэтому вы настроены.
Вы пытались использовать onFinishInflate
для запуска привязки? (Пример Котлина)
override fun onFinishInflate() {
super.onFinishInflate()
this.dataBinding = MyCustomBinding.bind(this)
}
Имейте в виду, что если вы используете привязку в своем представлении, она не сможет быть создана программно, по крайней мере, она будет очень запутана для поддержки обоих, даже если вы можете.
Ответ 3
Следуя решению, представленному george, графический редактор в студии Android больше не смог отобразить пользовательский вид. Причина в том, что в следующем коде фактически нет раздувания:
public MyCustomView(...) {
...
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mBinding = MyCustomViewBinding.inflate(inflater);
}
Я полагаю, что привязка обрабатывает инфляцию, однако графическому редактору это не понравилось.
В моем конкретном случае использования я хотел связать одно поле, а не всю модель представления. Я придумал (kotlin входящий):
class LikeButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
val layout: ConstraintLayout = LayoutInflater.from(context).inflate(R.layout.like_button, this, true) as ConstraintLayout
var numberOfLikes: Int = 0
set(value) {
field = value
layout.number_of_likes_tv.text = numberOfLikes.toString()
}
}
Символьная кнопка состоит из изображения и текстового вида. Текстовое представление содержит количество понравившихся, которые я хочу установить с помощью привязки данных.
Используя setter для numberOfLikes как атрибут в следующем xml, привязка данных автоматически делает ассоциацию:
<views.LikeButton
android:id="@+id/like_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:numberOfLikes="@{story.numberOfLikes}" />
Дополнительная литература: https://medium.com/google-developers/android-data-binding-custom-setters-55a25a7aea47
Ответ 4
Основываясь на том, что вы делаете, я пытался сделать MyCustomBinding.bind(this)
, и это больше не работало для меня. Связывание уже имело место, но мы вместо этого находим его.
private MyCustomBinding binding;
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
binding = DataBindingUtil.getBinding(this); //got the binding!
MyViewModelObject model = MyViewModelObject.get("myText");
binding.setVariable(BR.myViewModel, model);
}
Ответ 5
Здесь уже есть несколько хороших ответов, но я хотел предложить то, что я считаю самым простым.
Создайте свой собственный элемент управления с тегами макета, окружающими его, как и любой другой макет. Смотрите следующую панель инструментов, например. это используется в каждом из классов деятельности
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="YACustomPrefs" type="com.appstudio35.yourappstudio.models.YACustomPreference" />
</data>
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/YATheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
app:popupTheme="@style/YATheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
Теперь этот пользовательский макет является дочерним элементом каждого действия. Вы просто рассматриваете это как таковое в настройке привязки onCreate.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.yaCustomPrefs = YACustomPreference.getInstance(this)
binding.toolbarMain?.yaCustomPrefs = YACustomPreference.getInstance(this)
binding.navHeader?.yaCustomPrefs = YACustomPreference.getInstance(this)
binding.activity = this
binding.iBindingRecyclerView = this
binding.navHeader?.activity = this
//local pointer for notify txt badge
txtNotificationCountBadge = txtNotificationCount
//setup notify if returned from background so we can refresh the drawer items
AppLifeCycleTracker.getInstance().addAppToForegroundListener(this)
setupFilterableCategories()
setupNavigationDrawer()
}
Обратите внимание, что я устанавливаю дочерний контент одновременно с родительским, и все это делается с помощью доступа к точечной нотации. Пока файлы окружены тегами макета и вы назвали их, это легко сделать.
Теперь, если у пользовательского класса есть своя собственная привязка кода, тогда он может легко сделать собственную привязку в нем в onCreate или конструкторе, но вы получите картину. Если у вас есть свой собственный класс, просто добавьте следующее в конструктор, чтобы он соответствовал названному классу привязки. Он соответствует соглашению об именах файла макета в паскале, поэтому его легко найти и автоматически заполнить.
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mBinding = NameOfCustomControlBinding.inflate(inflater);
Надеюсь, это поможет.
Ответ 6
Привязка данных работает даже при объединении только родительский элемент должен быть "this" и присоединяться к родительскому true.
binding = DataBindingUtil.inflate(inflater, R.layout.view_toolbar, this, true)