androidx 与 Spinner 和自定义对象的数据绑定
androidx databinding with Spinner and custom objects
如何使用 androidx 数据绑定库用自定义对象列表填充 Spinner (app:entries)?以及如何为 Spinner (app:onItemSelected) 创建适当的 selection 回调?
我的布局:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type=".ui.editentry.EditEntryViewModel" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.editentry.EditEntryActivity">
<Spinner
android:id="@+id/spClubs"
android:layout_width="368dp"
android:layout_height="25dp"
app:entries="@{viewModel.projects}"
app:onItemSelected="@{viewModel.selectedProject}"
/>
</FrameLayout>
</layout>
EditEntryViewModel.kt
class EditEntryViewModel(repository: Repository) : ViewModel() {
/** BIND SPINNER DATA TO THESE PROJECTS **/
val projects : List<Project> = repository.getProjects()
/** BIND SELECTED PROJECT TO THIS VARIABLE **/
val selectedProject: Project;
}
Project.kt
data class Project(
var id: Int? = null,
var name: String = "",
var createdAt: String = "",
var updatedAt: String = ""
)
Spinner 应该显示每个项目的名称,当我 select 一个项目时,它应该保存在 viewModel.selectedProject 中。
LiveData 的使用是可选的。
我想我必须为 app:entries 编写一个@BindingAdapter 并为 app:onItemSelected 编写一个@InverseBindingAdapter。但是如果不为 Spinneradapter 编写通常的样板代码,我无法弄清楚如何实现它们...
你可以在fragment里面设置
binding.spinnerState.adapter = ArrayAdapter(
context!!,
R.layout.simple_spinner_item_1line,
viewModel.projects?.map { it.name }!!
)
请注意项目应该是
MutableLiveData<List<Projects>>()
好的,我想出了一个妥善的解决办法。这是带有一些解释的代码:
layout.xml
<Spinner
android:id="@+id/spProjects"
android:layout_width="368dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/spActivities"
app:projects="@{viewModel.projects}"
app:selectedProject="@={viewModel.entry.project}" />
app:projects
在我的 ViewModel
中绑定到 val projects: List<Project>
app:selectedProject
绑定到 val entry: Entry
,这是一个 class 具有 Project
作为 属性.
所以这是我的 ViewModel 的一部分:
class EditEntryViewModel() : ViewModel() {
var entry: MutableLiveData<Entry> = MutableLiveData()
var projects : List<Project> = repository.getProjects()
}
现在缺少的是实现以下功能的 BindingAdapter 和 InverseBindingAdapter:
- Spinner 应该列出来自 repsitory 的所有项目
- Spinner 应该预先 select select 当前
entry
的项目
- select编辑新项目时,应自动将其设置为
entry
绑定适配器
/**
* 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?, selectedProject: Project, listener: InverseBindingListener) {
if (projects == null) return
spinner.adapter = NameAdapter(spinner.context, android.R.layout.simple_spinner_dropdown_item, projects)
setCurrentSelection(spinner, selectedProject)
setSpinnerListener(spinner, listener)
}
您可以将 BindingAdapter 放在空文件中。它不必是任何 class 的一部分。
重要的是它的参数。它们由 BindingAdapters value
s 扣除。在本例中,值为 projects
、selectedProject
和 selectedProjectAttrChanged
。前两个参数对应我们自己定义的两个layout-xml属性。 last/third 参数是 DataBinding 过程的一部分:对于每个具有双向数据绑定的布局-xml 属性(即@={),都会生成一个值名字 <attribute-name>AttrChanged
这个特殊情况的另一个重要部分是 NameAdapter
,它是我自己的 SpinnerAdapter,它能够将我的项目作为项目保存,并且只在 name
属性 中显示它们=88=]。这样我们总是可以访问整个项目实例,而不是只能访问一个字符串(默认的 SpinnerAdapter 通常是这种情况)。
这是我的自定义 Spinner 适配器的代码:
名称适配器
class NameAdapter(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
}
}
现在我们有了一个保存整个项目信息的 Spinner,InverseBindingAdapter 就很简单了。它用于告诉 DataBinding 库应该设置什么值,从 UI 到实际的 class 属性 viewModel.entry.project
:
InverseBindingAdapter
@InverseBindingAdapter(attribute = "selectedProject")
fun getSelectedProject(spinner: Spinner): Project {
return spinner.selectedItem as Project
}
就是这样。所有的工作都很顺利。需要提及的一件事是,如果您的 List 包含大量数据,则不推荐使用此方法,因为所有这些数据都存储在适配器中。在我的例子中,它只是一些字符串字段,所以应该没问题。
为了完成,我想从 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: HasNameField): Boolean {
for (index in 0 until spinner.adapter.count) {
if (spinner.getItemAtPosition(index) == selectedItem.name) {
spinner.setSelection(index)
return true
}
}
return false
}
这个问题和回复对我过去几天努力解决类似问题非常有帮助。本着分享的精神,这里是我所有的文件:
MainActivity.kt
package com.mandal.mvvmspinnerviewbindingexample
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import com.mandal.mvvmspinnerviewbindingexample.databinding.ActivityMainBinding
import com.mandal.mvvmspinnerviewbindingexample.viewmodel.UserViewModel
class MainActivity : AppCompatActivity() {
/**
* Lazily initialize our [UserViewModel].
*/
private val viewModel: UserViewModel by lazy {
ViewModelProvider(this).get(UserViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
enter code here
val binding: ActivityMainBinding =
DataBindingUtil.setContentView(this, R.layout.activity_main)
// Allows Data Binding to Observe LiveData with the lifecycle of this Activity
binding.lifecycleOwner = this
// Giving the binding access to the UserViewModel
binding.viewModel = viewModel
}
}
User.kt
package com.mandal.mvvmspinnerviewbindingexample.model
data class User(
val id: Int,
val name: String,
) {
override fun toString(): String = name
}
Entry.kt
package com.mandal.mvvmspinnerviewbindingexample.model
data class Entry (var user: User)
NameAdapter.kt
package com.mandal.mvvmspinnerviewbindingexample.adapter
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.TextView
import com.mandal.mvvmspinnerviewbindingexample.model.User
class NameAdapter(context: Context, textViewResourceId: Int, private val values: List<User>) : ArrayAdapter<User>(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
}
}
UserViewModel.kt
package com.mandal.mvvmspinnerviewbindingexample.viewmodel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.mandal.mvvmspinnerviewbindingexample.model.Entry
import com.mandal.mvvmspinnerviewbindingexample.model.User
class UserViewModel: ViewModel (){
var users : List<User> = getUserList()
var entry: MutableLiveData<Entry> = MutableLiveData()
/**
* Sets the value of the status LiveData to the Mars API status.
*/
private fun getUserList() : List<User>{
//Setup Users
val user1 = User(1, "John")
val user2 = User(2, "Mary")
val user3 = User(2, "Patrick")
val user4 = User(2, "Amanda")
//Setup User List
val list = arrayListOf<User>(user1, user2, user3, user4)
return list
}
}
BindingAdapters.kt
package com.mandal.mvvmspinnerviewbindingexample
import android.R
import android.view.View
import android.widget.AdapterView
import android.widget.Spinner
import android.widget.Toast
import androidx.databinding.BindingAdapter
import androidx.databinding.InverseBindingAdapter
import androidx.databinding.InverseBindingListener
import com.mandal.mvvmspinnerviewbindingexample.adapter.NameAdapter
import com.mandal.mvvmspinnerviewbindingexample.model.User
/**
* fill the Spinner with all available projects.
* Set the Spinner selection to selectedProject.
* If the selection changes, call the InverseBindingAdapter
*/
@BindingAdapter(value = ["users", "selectedUser", "selectedUserAttrChanged"], requireAll = false)
fun setUsers(spinner: Spinner, users: List<User>?, selectedUser: User?, listener: InverseBindingListener) {
if (users == null) return
spinner.adapter = NameAdapter(spinner.context, R.layout.simple_spinner_dropdown_item, users)
setCurrentSelection(spinner, selectedUser)
setSpinnerListener(spinner, listener)
}
@InverseBindingAdapter(attribute = "selectedUser")
fun getSelectedUser(spinner: Spinner): User {
Toast.makeText(
spinner.context,
(spinner.selectedItem as User).name,
Toast.LENGTH_LONG
)
.show()
return spinner.selectedItem as User
}
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: User?): Boolean {
for (index in 0 until spinner.adapter.count) {
if (spinner.getItemAtPosition(index) == selectedItem?.name) {
spinner.setSelection(index)
return true
}
}
return false
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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="viewModel"
type="com.mandal.mvvmspinnerviewbindingexample.viewmodel.UserViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!--Spinner widget-->
<Spinner
android:id="@+id/userSpinner"
android:layout_width="160dp"
android:layout_height="50dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.17"
app:users="@{viewModel.users}"
app:selectedUser="@={viewModel.entry.user}"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mandal.mvvmspinnerviewbindingexample" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name="com.mandal.mvvmspinnerviewbindingexample.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
如何使用 androidx 数据绑定库用自定义对象列表填充 Spinner (app:entries)?以及如何为 Spinner (app:onItemSelected) 创建适当的 selection 回调?
我的布局:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type=".ui.editentry.EditEntryViewModel" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.editentry.EditEntryActivity">
<Spinner
android:id="@+id/spClubs"
android:layout_width="368dp"
android:layout_height="25dp"
app:entries="@{viewModel.projects}"
app:onItemSelected="@{viewModel.selectedProject}"
/>
</FrameLayout>
</layout>
EditEntryViewModel.kt
class EditEntryViewModel(repository: Repository) : ViewModel() {
/** BIND SPINNER DATA TO THESE PROJECTS **/
val projects : List<Project> = repository.getProjects()
/** BIND SELECTED PROJECT TO THIS VARIABLE **/
val selectedProject: Project;
}
Project.kt
data class Project(
var id: Int? = null,
var name: String = "",
var createdAt: String = "",
var updatedAt: String = ""
)
Spinner 应该显示每个项目的名称,当我 select 一个项目时,它应该保存在 viewModel.selectedProject 中。 LiveData 的使用是可选的。
我想我必须为 app:entries 编写一个@BindingAdapter 并为 app:onItemSelected 编写一个@InverseBindingAdapter。但是如果不为 Spinneradapter 编写通常的样板代码,我无法弄清楚如何实现它们...
你可以在fragment里面设置
binding.spinnerState.adapter = ArrayAdapter(
context!!,
R.layout.simple_spinner_item_1line,
viewModel.projects?.map { it.name }!!
)
请注意项目应该是
MutableLiveData<List<Projects>>()
好的,我想出了一个妥善的解决办法。这是带有一些解释的代码:
layout.xml
<Spinner
android:id="@+id/spProjects"
android:layout_width="368dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/spActivities"
app:projects="@{viewModel.projects}"
app:selectedProject="@={viewModel.entry.project}" />
app:projects
在我的 ViewModel
val projects: List<Project>
app:selectedProject
绑定到 val entry: Entry
,这是一个 class 具有 Project
作为 属性.
所以这是我的 ViewModel 的一部分:
class EditEntryViewModel() : ViewModel() {
var entry: MutableLiveData<Entry> = MutableLiveData()
var projects : List<Project> = repository.getProjects()
}
现在缺少的是实现以下功能的 BindingAdapter 和 InverseBindingAdapter:
- Spinner 应该列出来自 repsitory 的所有项目
- Spinner 应该预先 select select 当前
entry
的项目
- select编辑新项目时,应自动将其设置为
entry
绑定适配器
/** * 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?, selectedProject: Project, listener: InverseBindingListener) { if (projects == null) return spinner.adapter = NameAdapter(spinner.context, android.R.layout.simple_spinner_dropdown_item, projects) setCurrentSelection(spinner, selectedProject) setSpinnerListener(spinner, listener) }
您可以将 BindingAdapter 放在空文件中。它不必是任何 class 的一部分。
重要的是它的参数。它们由 BindingAdapters value
s 扣除。在本例中,值为 projects
、selectedProject
和 selectedProjectAttrChanged
。前两个参数对应我们自己定义的两个layout-xml属性。 last/third 参数是 DataBinding 过程的一部分:对于每个具有双向数据绑定的布局-xml 属性(即@={),都会生成一个值名字 <attribute-name>AttrChanged
这个特殊情况的另一个重要部分是 NameAdapter
,它是我自己的 SpinnerAdapter,它能够将我的项目作为项目保存,并且只在 name
属性 中显示它们=88=]。这样我们总是可以访问整个项目实例,而不是只能访问一个字符串(默认的 SpinnerAdapter 通常是这种情况)。
这是我的自定义 Spinner 适配器的代码:
名称适配器
class NameAdapter(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
}
}
现在我们有了一个保存整个项目信息的 Spinner,InverseBindingAdapter 就很简单了。它用于告诉 DataBinding 库应该设置什么值,从 UI 到实际的 class 属性 viewModel.entry.project
:
InverseBindingAdapter
@InverseBindingAdapter(attribute = "selectedProject") fun getSelectedProject(spinner: Spinner): Project { return spinner.selectedItem as Project }
就是这样。所有的工作都很顺利。需要提及的一件事是,如果您的 List 包含大量数据,则不推荐使用此方法,因为所有这些数据都存储在适配器中。在我的例子中,它只是一些字符串字段,所以应该没问题。
为了完成,我想从 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: HasNameField): Boolean {
for (index in 0 until spinner.adapter.count) {
if (spinner.getItemAtPosition(index) == selectedItem.name) {
spinner.setSelection(index)
return true
}
}
return false
}
这个问题和回复对我过去几天努力解决类似问题非常有帮助。本着分享的精神,这里是我所有的文件:
MainActivity.kt
package com.mandal.mvvmspinnerviewbindingexample
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import com.mandal.mvvmspinnerviewbindingexample.databinding.ActivityMainBinding
import com.mandal.mvvmspinnerviewbindingexample.viewmodel.UserViewModel
class MainActivity : AppCompatActivity() {
/**
* Lazily initialize our [UserViewModel].
*/
private val viewModel: UserViewModel by lazy {
ViewModelProvider(this).get(UserViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
enter code here
val binding: ActivityMainBinding =
DataBindingUtil.setContentView(this, R.layout.activity_main)
// Allows Data Binding to Observe LiveData with the lifecycle of this Activity
binding.lifecycleOwner = this
// Giving the binding access to the UserViewModel
binding.viewModel = viewModel
}
}
User.kt
package com.mandal.mvvmspinnerviewbindingexample.model
data class User(
val id: Int,
val name: String,
) {
override fun toString(): String = name
}
Entry.kt
package com.mandal.mvvmspinnerviewbindingexample.model
data class Entry (var user: User)
NameAdapter.kt
package com.mandal.mvvmspinnerviewbindingexample.adapter
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.TextView
import com.mandal.mvvmspinnerviewbindingexample.model.User
class NameAdapter(context: Context, textViewResourceId: Int, private val values: List<User>) : ArrayAdapter<User>(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
}
}
UserViewModel.kt
package com.mandal.mvvmspinnerviewbindingexample.viewmodel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.mandal.mvvmspinnerviewbindingexample.model.Entry
import com.mandal.mvvmspinnerviewbindingexample.model.User
class UserViewModel: ViewModel (){
var users : List<User> = getUserList()
var entry: MutableLiveData<Entry> = MutableLiveData()
/**
* Sets the value of the status LiveData to the Mars API status.
*/
private fun getUserList() : List<User>{
//Setup Users
val user1 = User(1, "John")
val user2 = User(2, "Mary")
val user3 = User(2, "Patrick")
val user4 = User(2, "Amanda")
//Setup User List
val list = arrayListOf<User>(user1, user2, user3, user4)
return list
}
}
BindingAdapters.kt
package com.mandal.mvvmspinnerviewbindingexample
import android.R
import android.view.View
import android.widget.AdapterView
import android.widget.Spinner
import android.widget.Toast
import androidx.databinding.BindingAdapter
import androidx.databinding.InverseBindingAdapter
import androidx.databinding.InverseBindingListener
import com.mandal.mvvmspinnerviewbindingexample.adapter.NameAdapter
import com.mandal.mvvmspinnerviewbindingexample.model.User
/**
* fill the Spinner with all available projects.
* Set the Spinner selection to selectedProject.
* If the selection changes, call the InverseBindingAdapter
*/
@BindingAdapter(value = ["users", "selectedUser", "selectedUserAttrChanged"], requireAll = false)
fun setUsers(spinner: Spinner, users: List<User>?, selectedUser: User?, listener: InverseBindingListener) {
if (users == null) return
spinner.adapter = NameAdapter(spinner.context, R.layout.simple_spinner_dropdown_item, users)
setCurrentSelection(spinner, selectedUser)
setSpinnerListener(spinner, listener)
}
@InverseBindingAdapter(attribute = "selectedUser")
fun getSelectedUser(spinner: Spinner): User {
Toast.makeText(
spinner.context,
(spinner.selectedItem as User).name,
Toast.LENGTH_LONG
)
.show()
return spinner.selectedItem as User
}
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: User?): Boolean {
for (index in 0 until spinner.adapter.count) {
if (spinner.getItemAtPosition(index) == selectedItem?.name) {
spinner.setSelection(index)
return true
}
}
return false
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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="viewModel"
type="com.mandal.mvvmspinnerviewbindingexample.viewmodel.UserViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!--Spinner widget-->
<Spinner
android:id="@+id/userSpinner"
android:layout_width="160dp"
android:layout_height="50dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.17"
app:users="@{viewModel.users}"
app:selectedUser="@={viewModel.entry.user}"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mandal.mvvmspinnerviewbindingexample" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name="com.mandal.mvvmspinnerviewbindingexample.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>