How to save spinner state on device rotation using a ViewModel? - android

How do you restore the Spinners selected item index a view model?
Right now I have a setup where you set a selectedIndex property on the ViewModel within the spinners OnItemSelectedListener.
The problem I'm running into is that every time the Fragment recreates, the selected listener gets re-assigned to the spinner, and the OnItemSelectedListener get fired for index 0, overriding whatever is stored in the ViewModel.
I feel like I'm missing something super simple here.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Set the spinners onItemSelectedListener
binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
viewModel.setSelectedItem(p2)
}
override fun onNothingSelected(p0: AdapterView<*>?) {
TODO("Not yet implemented")
}
}
// Restore spinner to previously selected state
viewModel.getSelectedCompanyIndex().let {
binding.spinner.setSelection(it)
}
// Observe the spinner data.
viewModel.spinnerData.observe(this) { spinnerArray ->
ArrayAdapter(
requireContext(),
R.layout.simple_spinner_dropdown_item,
spinnerArray
).also {
binding.spinner.adapter = it
}
}

Related

How to retrieve the String of a selected item in spinner outside of onItemSelected method?

I need to retrieve the selected string in a spinner outside of the .onItemSelectedListener.
The dropdown menu contains "Each week, each month, each year" strings and I need to retrieve that selected item String in order use them in if conditionals outside of the function.
I've only seen people making Toasts in the onItemSelected function but this doesn't solve my problem.
This is my code:
val spinner = binding.tvAutoComplete
val powtarzanie = resources.getStringArray(R.array.powtarzanie)
val arrayAdapter = ArrayAdapter(requireContext(),
R.layout.dropdown_powtarzaj_item,
powtarzanie)
spinner.setAdapter(arrayAdapter)
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val itemText: String = parent?.getItemAtPosition(position).toString()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
}
One way and probably the most simple way to go about this is to define a global variable:
private var spinnerSelection: String? = null
and just change it in your onItemSelected implementation:
spinnerSelection = parent?.getItemAtPosition(pos).toString()
Example code showing the whole flow:
class MainActivity : AppCompatActivity(), AdapterView.OnItemSelectedListener {
private var spinnerSelection: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val spinner: Spinner = findViewById(R.id.spinner)
spinner.onItemSelectedListener = this
ArrayAdapter.createFromResource(
this,
R.array.numbers_array,
android.R.layout.simple_spinner_item
).also { adapter ->
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = adapter
}
val button: Button = findViewById(R.id.button)
button.setOnClickListener {
Log.d("MainActivity", "Currently selected item: $spinnerSelection")
}
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, pos: Int, id: Long) {
spinnerSelection = parent?.getItemAtPosition(pos).toString()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
//TODO("Not yet implemented")
}
}

How to return value from function in Kotlin Android

In my application I want used extension function and I want return String!
I set return value from function and I write below codes, but show me empty value!
My Extension function code :
fun Spinner.setupListWithAdapter(list: MutableList<String>): String {
var itemSelected = ""
val adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, list)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
this.adapter = adapter
this.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
itemSelected = list[p2]
Log.e("categoryLog","1 : " + itemSelected)
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
Log.e("categoryLog","2 : " +itemSelected)
return itemSelected
}
When show logs in logcat, first show 2 then show 1 !
Logs :
2022-08-12 14:49:59.261 12074-12074/com.my.app E/categoryLog: 2 :
2022-08-12 14:49:59.310 12074-12074/com.my.app E/categoryLog: 1 : Movies
Why first call log 2 then call log 1 ?!
I used this func in fragment and for this when used this in fragment show me empty value!
How can I fix it?
Change your extension function like below
fun Spinner.setupListWithAdapter(list: MutableList<String>,callback: String -> Unit){
var itemSelected = ""
val adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, list)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
this.adapter = adapter
this.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
itemSelected = list[p2]
callback(itemSelected)
Log.e("categoryLog","1 : " + itemSelected)
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
Log.e("categoryLog","2 : " +itemSelected)
}
Then in Activity or fragment
spinner.setupListWithAdapter(list){
// It will invoked when something is selected from spinner
}
Here's the direct answer to your question about why the logs are not in the order you expected:
Your listener is called repeatedly in the future each time items are selected. The current function that creates this listener doesn't wait for any of that to happen. It sets up the listener and returns right away before any items have been selected. It wouldn't make sense for it to wait, because then your app would be frozen, waiting for this function to return because the user has to select an item before the listener is called. But if your app was frozen, then the user could never make a selection, so it would be frozen forever.
The other answers show how you might fix it, but it depends what you're trying to do. It doesn't logically make sense for this function to return a String, because you don't know how many times in the future the user is going to select an item. It could be zero times, one time, or many times.
Use typealias for callback
typealias OnSelectedCategory = (value: String) -> Unit
Update your method with callback
fun Spinner.setupListWithAdapter(list: MutableList<String>,listener : OnSelectedCategory) {
var itemSelected = ""
val adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, list)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
this.adapter = adapter
this.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
itemSelected = list[p2]
Log.e("categoryLog","1 : " + itemSelected)
listener.invoke(itemSelected)
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
Log.e("categoryLog","2 : " +itemSelected)
}
call with spinner
spinner.setupListWithAdapter(list){ value ->
Log.e("categoryLog","2 : " +value)
}

Kotlin: function setSelection() doesn't work outside onItemSelected() function

I have a spinner and when I select something from the list some action happens:
val arrayAdapter =
ArrayAdapter(requireContext(), R.layout.dropdown_item, carBrandList)
binding.spinner.adapter = arrayAdapter
binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
adapterView: AdapterView<*>?,
view: View?,
position: Int,
id: Long,
) {
refuelList.clear()
realTimeUpdates(carIdList[position])
saveData(requireContext(), carIdList[position])
saveSpinnerPosition(requireContext(), position)
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
I want to store spinner position in Shared Preferences and it works.
I want to retrieve spinner position if an user opens particular fragment.
How can I use function setSelection() outside this code? setSelection() only works for me if it is in onItemSelected() function
It should work outside the onItemSelected(), just be sure that you saved the correct spinner position in Shared Preferences.

spinner crashing using onItemSelectedListener

Good day
I have a customized spinner and it shows the string array without any issue. But when I add the onItemSelectedListener my application will crash when loading the fragment.
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
auth = Firebase.auth
// show back button
val activity = activity as? MainActivity
activity?.supportActionBar?.setDisplayHomeAsUpEnabled(true)
val result = inflater.inflate(R.layout.fragment_new_key, container, false)
val spinner: Spinner = result.findViewById(R.id.spinner_Category)
ArrayAdapter.createFromResource(
requireContext(), R.array.keyCategory, R.layout.spinner_item
).also { adapter ->
spinner.adapter = adapter
}
// without adding the below, the application will work smoothly
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
TODO("Not yet implemented")
}
}
return result
}
Could you please help, is there something conflicting with the spinner that pushed the application to crash?
Thank you.
Remove these lines
TODO("Not yet implemented")
TODO throws NotImplementedError

Android Kotlin onItemSelectedListener for spinner not working

I have a spinner with some items (strings).
I want to add the selected items to a list. I read online that I should use the onItemSelectedListenerrather than the onItemClickListener.
I implemented this but I don't know how to complete the step of adding it to the list.
class NewKitListActivity : AppCompatActivity() {
var spinnerArray = arrayOf("Dumbell", "Punching Bag", "Yoga Ball", "Skipping Rope")
//var spinnerArray = arrayOf(DataService.kitList)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new_kit_list)
val spinner = newKitItemSpinner
val spinnerArrayAdapter = ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, spinnerArray)
//selected item will look like a spinner set from XML
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = spinnerArrayAdapter
spinner.onItemSelectedListener = object : OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
val selectedItem = parent.getItemAtPosition(position).toString()
if (selectedItem == "Add new category") {
// do your stuff
}
} // to close the onItemSelected
override fun onNothingSelected(parent: AdapterView<*>) {
}
}}}
Thanks
(in Kotlin)Use this code:
yourSpinner?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
}
}
Thanks this is helpful for me, Its working fine !
daysSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
}
}
Add extension function
fun Spinner.selected(action: (position:Int) -> Unit) {
this.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
action(position)
}
}
}
simple use
spinner.selected {
println(it) //selected position
}
string will not be able to check using '==' instead you need to use equals("string")
if (selectedItem.equals("Add new category")) {
// do your stuff
}
I implemented like this.
1. Create Empty Mutable List
2. Set onItemSelectedListner on spinner
3. When user select item add that to mutable list
Check my this answer for more info. It will help you: Android Koltin pass spinner values to mutable list
instead of:
var spinnerArray = arrayOf("Dumbell", "Punching Bag", "Yoga Ball", "Skipping Rope")
try
var spinnerArray = mutableListOf<String>("Dumbell", "Punching Bag", "Yoga Ball", "Skipping Rope")
just had the same situation when I was trying to get a sqlite tableĀ“s $_ID and populate the spinner with them
// Extends AdapterView.OnItemSelectedListener
class Dialogs : DialogFragment(), AdapterView.OnItemSelectedListener {}
//Somewhre in onCreate (I'm using databinding but you don't have to)
binding.spinnerDialogEstados.onItemSelectedListener = this
// then implement members...
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
TODO("Not yet implemented")
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}

Categories

Resources