I'm using AndroidStudio 4.1.1 and kotlin
I'm having issues trying to get the value of a selected item in a spinner view/item. My first attempt, and many threads say to do it this way, is:
val spinner = findViewById<Spinner>(R.id.wMuscleGroup) as Spinner
val selectedText = spinner.getSelectedItem().toString()
This does not work for me. selectedText shows nothing in a Toast or a println
I also tried this, which I also found many threads for:
val spinner = findViewById<Spinner>(R.id.wMuscleGroup) as Spinner
if (spinner != null) {
val adapter = ArrayAdapter( this, android.R.layout.simple_spinner_item, muscleGroups )
spinner.adapter = adapter
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
val selectedText = muscleGroups[position]
println("Muscle Group selected is $selectedText") // <-- this works
}
override fun onNothingSelected(parent: AdapterView<*>) {
// write code to perform some action
}
}
}
// selectedText is not seen here:
println("Muscle Group selected is $selectedText")
This works and I can see selectedString in my println, in the onItemSelected function inside the block. But, how do I get this value to be seen outside of this block of code? I need to use this value in a class object and a database write. I've tried declaring selectedString outside/above the if (spinner != null), but that does not seem to work either.
Thanks for any help.
Your Spinner does not have a value selected by default. Hence calling spinner.getSelectedItem() returns null and null.toString() returns an empty String.
Your code will work only if the user has selected an option from the Spinner first. You can set a initial value by using spinner.setSelection(position).
If you want to use the selected value outside the selection, you can use a global variable as follows:
val spinner = findViewById<Spinner>(R.id.wMuscleGroup) as Spinner
var selectedText = muscleGroups.first() // as default
if (spinner != null) {
val adapter = ArrayAdapter( this, android.R.layout.simple_spinner_item, muscleGroups )
spinner.adapter = adapter
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
selectedText = muscleGroups[position]
}
override fun onNothingSelected(parent: AdapterView<*>) {
}
}
}
If you want to try something with selectedText, you should use it inside onItemSelected function.
Related
I have a MutableList in my Android project where i'm adding an object called Articolo, then when a new item is added to that list i need to check if one item with same ID exist and if it does i need to update it's quantity.
The issue is that i'm trying to use MutableList.find to find the object with the same ID and when i find it i'm simply add the quantity to existing quantity but instead it remains immutable.
Here is my Articolo.kt
data class Articolo(var barcode: String, var qta: Int) {
constructor() : this ("", 0)
}
And here is my function where i'm adding data to MutableList
private var articoli = mutableListOf<Articolo>()
private fun addBarcode(barcode: String, qta: Int) {
if (barcode.isEmpty()) {
txtBarcode.requestFocus()
return;
}
articoli.find{
it.barcode == barcode
}?.qta?.plus(qta) ?:
articoli.add(Articolo(barcode, qta))
}
So if i add the first object like barcode: 1111, qty: 1 and then another same object instead of having one element array with qty 2 i still have qty 1..
That's because .plus(Int) returns a new value. You're not changing the property.
Instead you should do:
fun addBarcode(barcode: String, qta: Int) {
val existant = articoli.find { it.barcode == barcode }
if (existant != null) existant.qta += qta
else articoli.add(Articolo(barcode, qta))
}
#VaiTon86 has the answer (you're not actually changing the value in the Articolo object) but really, you should probably be using a Map here anyway:
maximum one of each item
lookup by some value (barcode)
that's a map!
There's a few ways you could implement it, here's one:
val articoli = mutableMapOf<String, Articolo>()
private fun addBarcode(barcode: String, qta: Int) {
articoli.getOrPut(barcode) { Articolo(barcode, 0) }
.let { it.qta += qta }
}
So the getOrPut just adds a new zero-quantity Articolo entry if there isn't already one, and then you add qta to what's already there for that entry.
How do I change the value of a variable using the spinner widget.
strings.xml
<string name="selected_item">Selected item:</string>
<string-array name="Languages">
<item>English</item>
<item>Latin</item>
<item>French</item>
</string-array>
mainactivity
val spinner = findViewById<Spinner>(R.id.spinner)
if (spinner != null) {
val adapter = ArrayAdapter(
this,
android.R.layout.simple_spinner_item, languages
)
spinner.adapter = adapter
spinner.onItemSelectedListener = object :
AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>,
view: View, position: Int, id: Long
) {
//what to do here?
}
}
}
suppose I want to change the value of this variable
strokeManager.lang = "en"
So you will want to use the position to access it from the list that you used to populate the spinner.
strokeManager.lang = languageMapToLangCode[languages.get(position)]
This should be defined in your class somewhere the spinner can access it
val languageMapToLangCode: HashMap<String, String> = hashMapOf("French" to "fr", "Latin" to "ln", "English to "en")
I am trying to display a text-file from res/raw into a textview with an onItemSelectedListener.
I can do this hardcoding but it would not be efficient because I have 10+ text-files. So I am trying to optimize my code and display the text-file based the index, which reads from the JSON file. However, when I tried this, nothing displays in the textviews and no errors are being thrown. It just says that the file cannot be found.
spSongs.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
adapterView: AdapterView<*>,
view: View,
i: Int,
l: Long
) {
songIndex = i //remember what the user chooses
try {
var x = myJSON_array.getJSONObject(songIndex)
.getString("filename")
x = x.substring(0, x.lastIndexOf("."))
val temp = resources.getIdentifier(
x,
"raw",
packageName
)
val input_stream =
baseContext.resources.openRawResource(temp)
val text = input_stream.readBytes()
.toString(Charset.defaultCharset())
tvlyrics.text = text
} catch (e: JSONException) {
e.printStackTrace()
}
//end of try-catch for painting JSON decode
}//end of onItemSelected of Painting spinner
override fun onNothingSelected(adapterView: AdapterView<*>) {}
}// end of setOnItemSelectedListener
thank you
Solved it, I was calling the wrong array inside the object.
Android Spinner is not working, the API is working and the Spinner's list of items is working. However, the item selection is not working.
class PlayerSignup2Activity : AppCompatActivity() {
private lateinit var positions : List<Position>
val positionSpinner = positionsSpinner
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(com.example.app.R.layout.activity_user_signup2)
//Positions from API
positions = APIService.getPositions(this)
val spinnerAdapter = ArrayAdapter(this, R.layout.spinner_item, positions)
spinnerAdapter.setDropDownViewResource(R.layout.spinner_item)
positionSpinner.adapter = spinnerAdapter
}
Spinner list
After selecting any item
Any idea on how to fix this?!
Try with this
<Spinner
android:id="#+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
In Kotlin
class MainActivity : /** Other Classes, */AdapterView.OnItemSelectedListener {
var list_of_items = arrayOf("Item 1", "Item 2", "Item 3")
override fun onCreate(savedInstanceState: Bundle?) {
spinner!!.setOnItemSelectedListener(this)
// Create an ArrayAdapter using a simple spinner layout and languages array
val aa = ArrayAdapter(this, android.R.layout.simple_spinner_item, list_of_items)
// Set layout to use when the list of choices appear
aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
// Set Adapter to Spinner
spinner!!.setAdapter(aa)
}
override fun onItemSelected(arg0: AdapterView<*>, arg1: View, position: Int, id:Long{
// use position to know the selected item
//here you will get the answwer
}
override fun onNothingSelected(arg0: AdapterView<*>) {
}
}
try using prompt for adding title in .XML file
<Spinner
android:id="#+id/spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:prompt="#string/spinner_title"/>
or try adding 1 more item that contains whatever title you want like "select position" on index 0.
positions.add("select position");
positions.add("value 1");
after adding values to list set spinner value to index 0 by default using below code.
mSpinner.setSelection(0) .
and on Item Selected check if index 0 or value is "select position" then ignore selection else perform action you want.
private lateinit var positions : List<Position>
//add dummy data first
positions.add(Position("select position"));
//Positions from API
positions.addAll(APIService.getPositions(this));
I know there have been several questions that dealt with the problem how to add the "Select one..." hint for the Spinner before the first selection is made. But that's not my case.
What I need is to display the hint only when the SpinnerAdapter is empty. By default in such case, nothing happens on click (but that is not the major problem), and most of all, the spinner doesn't display any text, so it looks like this, which obviously doesn't feel right:
Any idea how to simply handle this problem? I've come up with 2 possible solutions, but I don't like any of them very much:
If the SpinnerAdapter is empty, hide the Spinner from the layout and display a TextView with the same background as the Spinner instead.
Implement a custom SpinnerAdapter whose getCount() returns 1 instead of 0 if the internal list is empty, and at the same time, have its getView() return a TextView with the required "Empty" message, possibly grey-coloured. But that would require specific checking if the selected item is not the "Empty" one.
You can use this SpinnerWithHintAdapter class below
class SpinnerWithHintAdapter(context: Context, resource: Int = android.R.layout.simple_spinner_dropdown_item) :
ArrayAdapter<Any>(context, resource) {
override fun isEnabled(position: Int): Boolean {
return position != 0
}
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
return (super.getDropDownView(position, convertView, parent) as TextView).apply {
if (position == 0) {
// Set the hint text color gray
setTextColor(Color.GRAY)
} else {
setTextColor(Color.BLACK)
}
}
}
fun attachTo(spinner: Spinner, itemSelectedCallback: ((Any?) -> Unit)? = null) {
spinner.apply {
adapter = this#SpinnerWithHintAdapter
itemSelectedCallback?.let {
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val selectedItem = parent?.getItemAtPosition(position)
// If user change the default selection
// First item is disable and it is used for hint
if (position > 0) {
it(selectedItem)
}
}
}
}
}
}
}
How to use? Let's assume I have data class called City
data class City(
val id: Int,
val cityName: String,
val provinceId: Int
) {
/**
* By overriding toString function, you will show the dropdown text correctly
*/
override fun toString(): String {
return cityName
}
}
In the activity, initiate the adapter, add hint(first item), add main items, and finally attach it to your spinner.
SpinnerWithHintAdapter(this#MyActivity)
.apply {
// add hint
add("City")
// add your main items
for (city in cityList) add(city)
// attach this adapter to your spinner
attachTo(yourSpinner) { selectedItem -> // optional item selected listener
selectedItem?.apply {
if (selectedItem is City) {
// do what you want with the selected item
}
}
}
}