Nav*_*een 0 data-binding android android-layout kotlin
我有一个像这样在 xml 中定义的微调器
<Spinner
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/expense_category"
app:sourceData="@{()->createExpenseViewModel.getAllSourceItems(1)}"
app:layout_constraintStart_toStartOf="@+id/textView"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@+id/textView" app:layout_constraintWidth_percent="0.7"
/>
Run Code Online (Sandbox Code Playgroud)
createExpenseViewModel.getAllSourceItems(1) 这个方法返回 LiveData <List<Source>>,所以我为这种情况编写了一个绑定适配器
@BindingAdapter("app:sourceData")
fun setSourceData(spinner: Spinner, sourceList: List<Source>) {
val categoryItems = ArrayList<String>()
categoryItems.addAll(sourceList.map { it.sourceName })
val spinnerAdapter =
ArrayAdapter<String>(spinner.context, R.layout.simple_spinner_dropdown_item, categoryItems)
spinner.adapter = spinnerAdapter
}
Run Code Online (Sandbox Code Playgroud)
构建应用程序时,我收到以下错误,
****/ data binding error ****msg:Cannot find the proper callback class for app:sourceData. Tried java.util.List but it has 25 abstract methods, should have 1 abstract methods. file:/home/naveen/Desktop/project-expense/app/src/main/res/layout/activity_create_expense.xml loc:94:34 - 94:80 ****\ data binding error ****
这个错误实际上是什么意思,如何解决这个错误?
编辑:
我打算做的是获取实时数据返回的列表并转换为 ArrayList 类型,一旦 livedata 返回列表,我需要触发我的绑定适配器,但是如果我使用这个应用程序:sourceData="@{createExpenseViewModel.getAllSourceItems( 1)}" 并设置绑定适配器,适配器只获取空列表
您正在将一个方法绑定到app:sourceData,但您希望在绑定适配器中为它提供一个变量。那行不通。我猜您想将列表填充到微调器中。为此,我将在您的 viewModel 中创建一个属性并将此属性绑定到 xml 中。我在一个应用程序中做到了这一点,在该应用程序中,我有一个要在 Spinner 中显示的项目列表。这是包含 InverseBindingAdapter 的代码,用于自动将所选项目保存在 ViewModel 的另一个变量中。
视图模型:
// getProjects() returns the LiveData
val projects = metaDataRepository.getProjects()
// use _selectedProject only within ViewModel. Do not expose MediatorLiveData to UI.
// in UI observe selectedProject
private val _selectedProject = MediatorLiveData<Project>()
val selectedProject: LiveData<Project>
get() = _selectedProject
Run Code Online (Sandbox Code Playgroud)
布局 XML :
<Spinner
android:id="@+id/spProjects"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:projects="@{viewModel.projects}"
app:selectedProject="@={viewModel.selectedProject}" />
Run Code Online (Sandbox Code Playgroud)
BindingAdapter(将数据从 viewModel 填充到 UI):
/**
* fill the Spinner with all available projects.
* Set the Spinner selection to selectedProject.
* If the selection changes, call the InverseBindingAdapter
*/
@BindingAdapter(value = ["projects", "selectedProject", "selectedProjectAttrChanged"], requireAll = false)
fun setProjects(spinner: Spinner, projects: List<Project>?, selectedProject: Project, listener: InverseBindingListener) {
if (projects == null) return
spinner.adapter = ProjectAdapter(spinner.context, android.R.layout.simple_spinner_dropdown_item, projects)
setCurrentSelection(spinner, selectedProject)
setSpinnerListener(spinner, listener)
}
Run Code Online (Sandbox Code Playgroud)
BindingAdapter 的辅助方法:
private fun setSpinnerListener(spinner: Spinner, listener: InverseBindingListener) {
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) = listener.onChange()
override fun onNothingSelected(adapterView: AdapterView<*>) = listener.onChange()
}
}
private fun setCurrentSelection(spinner: Spinner, selectedItem: Project?): Boolean {
if (selectedItem == null) {
return false
}
for (index in 0 until spinner.adapter.count) {
val currentItem = spinner.getItemAtPosition(index) as Project
if (currentItem.name == selectedItem.name) {
spinner.setSelection(index)
return true
}
}
return false
}
Run Code Online (Sandbox Code Playgroud)
适合您的 Spinner 的简单适配器。将此更改为您的需要:
/**
* Adapter for displaying the name-field of an Project in a Spinner
*/
class ProjectAdapter(context: Context, textViewResourceId: Int, private val values: List<Project>) : ArrayAdapter<Project>(context, textViewResourceId, values) {
override fun getCount() = values.size
override fun getItem(position: Int) = values[position]
override fun getItemId(position: Int) = position.toLong()
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val label = super.getView(position, convertView, parent) as TextView
label.text = values[position].name
return label
}
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
val label = super.getDropDownView(position, convertView, parent) as TextView
label.text = values[position].name
return label
}
}
Run Code Online (Sandbox Code Playgroud)
InverseBindingAdapter(将选定的 Spinner 项存储在 viewModel 中)
/**
* get the selected projectName and use it to return a
* Project which is then used to set appEntry.value.project
*/
@InverseBindingAdapter(attribute = "selectedProject")
fun getSelectedProject(spinner: Spinner): Project {
return spinner.selectedItem as Project
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1564 次 |
| 最近记录: |