I have a spinner populated with array of strings from XML:
<Spinner
android:id="#+id/spinnerUnits"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:entries="#array/units"
app:layout_constraintStart_toEndOf="#+id/barrier3"
app:layout_constraintTop_toBottomOf="#+id/labelGeneric" />
When user selects something, I save it to SharedPreferences.
How do I set the value back when the app is opened next time? I have only the value saved, not its position in spinner's list.ยด
If I just do spinner.Units.adapter to get the adapter, how does one safely cast SpinnerAdapter! to ArrayAdapter? Or is this wrong approach?
Well you need to post the adapter code as well but here I am giving a sample how I will do in adapter.
val adapterGender = ArrayAdapter<String>(context, layoutCode, context.resources.getStringArray(R.array.genderArray))
override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
val data = listOfItems[holder!!.adapterPosition]
holder.etName?.setText(data.name)
holder.spGender?.adapter = adapterGender
holder.spGender?.setSelection(adapterGender.getPosition(data.gender))
}
{
val etName: AppCompatEditText? = itemView?.findViewById(R.id.etName)
val spGender: AppCompatSpinner? = itemView?.findViewById(R.id.spGender)
init {
//listener for gender selection
spGender?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(p0: AdapterView<*>?) {
}
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
listOfItems[adapterPosition].gender = p0?.selectedItem.toString()
Log.d("TAG", p0?.selectedItem.toString())
}
}
}
}
so basically I am setting a onItemSelectedListener on spinner and listening for value changes and then setting it in model/pojo for persisting and assigning in onBindViewHolder for setting back values.
Bit late to the party, but for everyone who may be looking to a more compact and/or prettier solution; try querying the array you put in the adapter.
Assuming you've saved the values array separately, either in XML or in a class field, and you know the selected value but not the position, you can get the position using the indexOf method and first {...} query.
So, taking your example, one could achieve this like so:
fun setSpinnerSelectionByValue(value: String) {
val xmlArray: Array<String> = context.resources.getStringArray(R.array.units) // get array from resources
val spinner = findViewById<Spinner>(R.id.spinnerUnits) // get the spinner element
spinner.setSelection(xmlArray.indexOf(
xmlArray.first { elem -> elem == value } // find first element in array equal to value
)) // get index of found element and use it as the position to set spinner to.
}
The first query will throw a NoSuchElement exception if no element in the array is equal to the value parameter, but it will be ignored and the position will default to 0.
If some magic is done on the array before inserting it into the spinner, the most common of which being inserting a null selector, there is most likely still a way of getting the correct position using the above technique.
When a null selector is inserted, for example, you could simply add + 1 just after the indexOf method and still get the correct position in the spinner.
Related
I have succeeded pulling JSON from Reddit API but I cannot put it into my RecyclerView. I set a Log to check if my JSON is empty or null, and the Log successfully print the desired output, so that means my JSON is not empty and contains the necessary data.
Here is my PostRowAdapter.kt
class PostRowAdapter(private val viewModel: MainViewModel)
: ListAdapter<RedditPost, PostRowAdapter.VH>(RedditDiff()) {
private var awwRow = listOf<RedditPost>()
override fun onBindViewHolder(holder: VH, position: Int) {
val binding = holder.binding
awwRow[position].let{
binding.title.text = it.title
}
}
override fun getItemCount() = awwRow.size
}
I thought my code was correct, but when I ran the app, the RecyclerView still blank. Where am I wrong?
your PostRowAdapter is populating awwRow list, which is empty on start and never updated, thus this RecyclerView will always contain 0 elements
if you are using ListAdapter and submitList(...) method then you shouldn't override getItemCount and you shouldn't have own data list, so remove these lines
private var awwRow = listOf<RedditPost>() // on top
override fun getItemCount() = awwRow.size // on bottom
if you want access to whole list set with submitList() method then you can call inside adapter getCurrentList(). if you need a particular item at position (like in onBindViewHolder) then use getItem(position). So instead of
awwRow[position].let{
you should have
getItem(position).let{
I am having an issue with RecyclerView - in onBindViewHolder(), when I make a condition for each item in the list (going through the whole list, position by position), and want to change the item text value, it doesn't work. I assume that's because RecyclerView recycles items.
`
override fun onBindViewHolder(holder: TimeViewHolder, position: Int) {
val time = holder.itemView.findViewById<TextView>(R.id.timeTextView)
val item = list[position]
//items in list: "11aa", "11bb", "11cc"
if (item.value.substring(0, 2).toInt() == 11 && item.value.substring(2,4) == "bb") {
time.text = "true"
} else {
time.text="false"
}
// list should be shown as: "false", "true", "false"
if (item.value.substring(0,2).toInt() == 11 && item.value.substring(2,4) == "aa") {
time.text="true"
} else {
time.text="false"
}
//list should be shown as: "true", "false", "false"
`
Example: item is "11bb" -> show true, all other items should be false (including "11aa")
item is "11aa" -> show true, all other items are false (including "11bb")
I would greatly appreciate any suggestion!
I tried the else condition, so it is not only "if" without other possible outcome, still doesn't work
For every item in the list, you're doing two checks. First you check if item is "11bb" and display true or false. But then you throw that result out the window by checking if it's "11aa" instead, and display true or false based on that.
The end result is each item displays true if it's "11aa", otherwise it shows false.
Here's what you said you wanted in your question:
Example: item is "11bb" -> show true, all other items should be false (including "11aa")
item is "11aa" -> show true, all other items are false (including "11bb")
So it sounds like you want a single item to display true, and displaying that sets the others to false, right?
Those two rules you've provided contradict each other - what if you have both "11aa" and "11bb" in your data, like your example does?
//items in list: "11aa", "11bb", "11cc"
Which one should show true? The first one in the list, "11aa"? Or does a later one override the earlier one, so "11bb"? What's the rule for resolving this?
Here's a way you can do it, assuming you want the first one in the list.
First, you need a function to set your adapter's data. This is so you can check through it, and work out in advance which item needs to be set to true:
// inside your adapter
// your internal data - private so anything setting the data -has to- go through
// the setter function below.
private var items: List<String>
// index of the item that should be displayed as true, or null if there isn't one
// (I'm storing an index instead of the item in case there are duplicates in the list)
private val trueItemPosition: Int? = null
fun setData(newData: List<String>) {
// set the new data
items = newData
// store the index of the first item that matches your rules, or null if none do
val matchedIndex = items.indexOfFirst(::matchesRule)
trueItemPosition = if (matchedIndex == -1) null else matchedIndex
// always refresh after setting data!
notifyDataSetChanged()
}
// you don't need to do any of that Int conversion or substring stuff, just match the string!
private fun matchesRule(item: String) = when {
item.startsWith("11aa") -> true
item.startsWith("11bb") -> true
else -> false
}
Then in onBindViewHolder you can decide what to do depending on the current item's position and the value of trueItemPosition:
// in onBindViewHolder
when(trueItemPosition) {
null -> {
// display your item normally - there's no 11aa or 11bb in the list
}
position -> {
// this item is the matching one
time.text = "true"
}
else -> {
// there's a matching item, but this isn't it
time.text = "false"
}
}
And that's pretty much it! You can use indexOfLast if you want the last matching item to take precedence, and you could make separate functions for the different rule matchers if you want, say, a 11aa anywhere in the list to take precedence over any 11bbs. Just run them one after another on the whole data set in order of preference, until you get a matching index.
Like I said, this relies on you setting all your data through that setData function, so it can work things out before you display anything. That includes the initial data you provide - so if you're passing data in as a constructor parameter, that can't be a property and you need to call setData with that parameter in an init block:
class MyAdapter(
...
data: List<String> // not a val/var, we're just using this temporarily
... {
init {
// set the internal data through the setter
setData(data)
}
}
and that should do it!
edit If you need arbitrary matching indices (other than first or last) you'll need to code that logic - it's probably best to put it in a function:
fun <T> Iterable<T>.matchingIndexNumber(ordinal: Int, predicate: (T) -> Boolean) =
mapIndexedNotNull { i, item -> if (predicate(item)) i else null }
.getOrNull(ordinal - 1) ?: -1
Now you can call items.matchingIndexNumber(2, ::matchesRule) to get the 2nd match's index, etc.
By the way, I kinda glossed over this, but the reason I used indices was because they're guaranteed to be unique - if you know all your items will be unique objects (i.e. none of them return true for equals when compared to the other items) then you can drop the indices entirely, and just store the matching object:
// potentially store the important -item- itself
private val trueItem: String? = null
fun setData(newData: List<String>) {
...
// just set the first item that matches, or null if nothing matches
trueItem = items.first(::matchesRule)
...
}
override fun onBindViewHolder(holder: TimeViewHolder, position: Int) {
...
when(trueItem) {
null -> // no important item in list - display this normally
item -> // this item is the important one
else -> // there's an important item, this isn't it
}
...
}
Which is a whole lot simpler! The indices approach works no matter what, but if your items are all unique, this is the way I'd go. (If they're not, e.g. you have two "11aa" strings, they'll both match trueItem in that when block so they'll both show true or whatever)
Anyway, if you're working with the items themselves instead of having to juggle indices and return -1 for missing stuff, you can just do this:
// get the 3rd matching item
trueItem = items.filter(::matchesRule).getOrNull(3)
// or
trueItem = items.filter(::matchesRule).drop(2).firstOrNull()
// or if you want the 3rd item, but if there isn't one you want the 2nd, and so on
trueItem = items.filter(::matchesRule).take(3).lastOrNull()
There are way more options in the standard library for working with the collection of items directly, it's much more flexible, so I'd recommend it if you can!
(You could also do the take(count).lastOrNull() trick in that function I wrote if you do want/need to stick with the indices though)
In my case I am trying to get a list of String from my Adapter and use it in my Fragment but upon debugging using Logs I found that the list is getting updated inside the onBindViewHolder but not outside it. So when I try to access the list from my Fragment I am getting an empty list of String.
I have spent few hours trying to figure this but can't find a feasible solution.
My Approach: I am thinking of an approach to save this list in a room table and then query it back in the Fragment. Though it may solve the issue but is it the only way? Are there any other ways to achieve this result?
My Adapter
class FloorProfileDialogAdapter() : RecyclerView.Adapter<FloorProfileDialogAdapter.MyViewHolder>() {
var floors = emptyList<String>()
inner class MyViewHolder(val binding: ScheduleFloorDialogItemBinding) :
RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ScheduleFloorDialogItemBinding.inflate(inflater, parent, false)
return MyViewHolder(binding)
}
private val checkedFloors: MutableList<String> = mutableListOf()
//List of uniquely selected checkbox to be observed from New Schedule Floor Fragment
var unique: List<String> = mutableListOf()
#SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentFloor = floors[position]
Timber.d("Current floor: $currentFloor")
holder.binding.floorCheckBox.text = "Floor $currentFloor"
//Checks the checked boxes and updates the list
holder.binding.floorCheckBox.setOnCheckedChangeListener { buttonView, isChecked ->
if (buttonView.isChecked) {
Timber.d("${buttonView.text} checked")
checkedFloors.add(buttonView.text.toString())
} else if (!buttonView.isChecked) {
Timber.d("${buttonView.text} unchecked")
checkedFloors.remove(buttonView.text)
}
unique = checkedFloors.distinct().sorted()
Timber.d("List: $unique")
}
}
fun returnList(): List<String> {
Timber.d("$unique")
return unique
}
override fun getItemCount(): Int {
return floors.size
}
#SuppressLint("NotifyDataSetChanged")
fun getAllFloors(floorsReceived: List<String>) {
Timber.d("Floors received : $floorsReceived")
this.floors = floorsReceived
notifyDataSetChanged()
}
}
Fragment code where I am trying to read it
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//Chosen Floors
val chosenFloors = floorProfileDialogAdapter.returnList()
Timber.d("Chosen floors : $chosenFloors")
}
Note: The list I am trying to receive is var unique: List<String> = mutableListOf. I tried to get it using the returnList() but the log in that function shows that list is empty. Similarly the Log in fragment shows that it received an empty list.
Edit 1 :
Class to fill the Adapter Floors using getAllFloors()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val floorList: MutableList<String> = mutableListOf()
var profileName: String? = ""
profileName = args.profileName
//Profile name received
Timber.d("Profile name : $profileName")
//Getting list of all floors
createProfileViewModel.totalFloors.observe(viewLifecycleOwner) {
Timber.d("List of floors received : $it")
val intList = it.map(String::toInt)
val maxFloorValue = intList.last()
var count = 0
try {
while (count <= maxFloorValue) {
floorList.add(count.toString())
count++
}
} catch (e: Exception) {
Timber.d("Exception: $e")
}
floorProfileDialogAdapter.getAllFloors(floorList)
Timber.d("Floor List : $floorList")
}
When you first set up your Fragment and create your Adapter, unique is empty:
var unique: List<String> = mutableListOf()
(if you have some checked state you want to save and restore, you'll have to initialise this with your checked data)
In onViewCreated, during Fragment setup, you get a reference to this (empty) list:
// Fragment onViewCreated
val chosenFloors = floorProfileDialogAdapter.returnList()
// Adapter
fun returnList(): List<String> {
return unique
}
So chosenFloors is a reference to this initial entry list. But when you actually update unique in onBindViewHolder
unique = checkedFloors.distinct().sorted()
you're replacing the current list with a new list object. You're not updating the existing list (even though you made it a MutableList). So you never actually add anything to that empty list you started with, and chosenFloors is left pointing at a list that contains nothing, while the Adapter has discarded it and unique holds a completely different object.
The solution there is to make unique a val (so you can't replace it) and just change its contents, e.g.
unique.clear()
unique += checkedFloors.distinct().sorted()
But I don't feel like that's your problem. Like I pointed out, that list is initially empty anyway, and you're grabbing it in your Fragment during initialisation just so you can print out its contents, as though you expect it to contain something at that point. Unless you initialise it with some values, it's gonna be empty.
If you're not already storing/restoring them, you'll need to handle that! I posted some code to do that on another answer so I'll just link that instead of repeating myself. That code is storing indices though, not text labels like you're doing. Indices are much cleaner and avoid errors - the text is more of a display thing, a property of the item the specific (and unique) index refers to. (But you can store a string array in SharedPreferences if you really want to.)
Also you're not actually updating your ViewHolder to display the checked state for the current item in onBindViewHolder. So whatever ViewHolder you happen to have been given (there's only a few of them for the list, they get reused) it's just showing whatever its checkbox was last set to, by you poking at it. Check an item, then scroll the list and see what happens!
So you need to check or uncheck the box so it's correct for the item you're displaying. This is pretty easy if you're storing the checked items by indices:
// explicitly set the checked state, depending on whether the item at 'position' is checked
holder.binding.floorCheckBox.checked = checkedItems[position]
You can work out something similar for your text label approach, but again I wouldn't recommend doing things that way.
I am building an app where user is required to fill some data in order to post something, so a fragment consists of EditText, radio buttons and Spinner along with RecyclerView which dynamically renders a number of child layout containing TextView and EditText.
So when user select category from Spinner, some properties which are related to that category are displayed in RecyclerView and user can optionally fill some of them.
I have tried to implement this functionality using callback and TextWatcher but I don't get the values I want.
CallBack
interface PropertiesCallback {
fun addProp(position: Int, title: String, value: String)
}
Adapter
class PropertiesAdapter(private val propertiesCallback: PropertiesCallback)
: RecyclerView.Adapter<PropertiesAdapter.ViewHolder>() {
private var list = listOf<CategoriesAndSubcategoriesQuery.Property>()
fun setData(listOfProps: List<CategoriesAndSubcategoriesQuery.Property>) {
this.list = listOfProps
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.z_property_input, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int = list.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(list[position], position)
}
inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
private val label: TextView = view.findViewById(R.id.label)
private val input: EditText = view.findViewById(R.id.input)
fun bind(prop: CategoriesAndSubcategoriesQuery.Property, position: Int) {
label.text = prop.title()
prop.hint()?.let { input.hint = prop.hint() }
input.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
propertiesCallback.addProp(position, prop.title(), input.text.toString())
}
})
}
}
}
In Fragment
private var propertiesList = mutableListOf<CategoriesAndSubcategoriesQuery.Property>()
private var propertiesInputList = mutableListOf<ProductPropertiesInput>()
private fun setUpSubcategorySpinner() {
subcategoriesAdapter = ArrayAdapter(
this#AddProductFragment.context!!,
android.R.layout.simple_spinner_item,
subcategoriesList
)
//Subcategories
subcategoriesAdapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line)
subcategory_spinner.adapter = subcategoriesAdapter
subcategory_spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
subcategoryId = subcategoriesList[position].id()
//Adding properties
subcategoriesList[position].properties()?.let {
//Clear previous properties data of another subcategory.
propertiesInputList.clear()
propertiesList.clear()
propertiesList.addAll(it)
propertiesAdapter.setData(propertiesList)
propertiesAdapter.notifyDataSetChanged()
}
}
override fun onNothingSelected(parent: AdapterView<*>) {}
}
}
overide
override fun addProp(position: Int, title: String, value: String) {
val prop = ProductPropertiesInput
.builder()
.title(title)
.value(value)
.build()
propertiesInputList.add(prop)
//Log.d(TAG, "prop: ${prop.title()} : ${prop.value()}")
}
submit fun
private fun submitProduct() {
//Initializing properties.
val properties: Any
//The keys needed in final list.
val propertyKeys = propertiesList.map { it.title() }
//Removing objects which keys are not needed.
propertiesInputList.removeAll { it.title() !in propertyKeys }
Log.d(TAG, "propertiesInputList: $propertiesInputList")
//Removing duplicate and assign result in properties var.
properties = propertiesInputList
.distinctBy { it.title() }
Log.d(TAG, "properties: $properties")
for (prop in properties) {
Log.d(TAG, "properties , title: ${prop.title()}, value: ${prop.value()} ")
}
}
Above codes is intended to work as. When user types a value in one of the EditText in RecyclerView the value will be taken to fragment and added to an object which takes title and value and then added to propertiesInputList.
Problem 1: propertiesInputList will have so many duplicates objects with the same title and I thought the best solution was using distinctBy.
Problem 2: When user fills a number of EditText which are related to let's say category1 and changes his mind and select another category from Spinner. The previous values which are not part of new chosen category remain in propertiesInputList list. So I thought the best solution was to clear propertiesInputList and using removeAll with the titles related to category to filter unwanted objects.
But now I get only the first letter user types. If user types shoes I get s. So it seems distinctBy returns the first object but I want to get exactly last word user typed and if the user typed and erased everything I want blank.
Is there a better solution to handle this? Like looping recyclerView only when user press submit instead of TextWatcher? Or which part should I fix to make this work?
I don't completely understand what you are trying to achieve here. EditTexts inside a RecyclerView is generally not a good idea for following reasons.
When the recyclerView is scrolled, you would want to preserve the
text added by the user for that particular field/item and show it
correctly when the user scrolls back.
When you add a TextWatcher to an EditText, you also need to remove it when the view is recycled or the view holder is bound again. Otherwise, you will end up with multiple listeners and things will go wrong.
For the other question that you have,
But now I get only the first letter user types. If user types shoes I get s
That's by design. TextWatcher would emit event every time a character is entered. So you would get s, sh, sho, shoe, shoes. So you can not take an action on this data because the user is still adding something to that field.
So,
You don't know when the user has stopped adding the text to the EditText (or whether user is done). You could use something like debounce but that is complicated. You should give a button to the user. Take the value when the user taps the button.
I am assuming you have multiple edittexts in the RecyclerView. So you would need to store the values for each edittext because the recyclerview will re-use the views and you'll lose the data. You could do that in your adapter's onViewRecycled callback. Keep a map of id -> string where you store this data and retrieve when the view holder is bound.
You could also use a TextWatcher but you would have detach it before attaching a new one or in onViewRecycled.
Update:
If I had something like this, I would use a ScrollView with a vertical LinearLayout (for simplicity) and add EditText based on the requirements. If you want to add TextWatcher, you'd need some kind of stable id.
class EditTextContainer : LinearLayout {
private val views = mutableListOf<EditText>()
private val textWatchers = hashMapOf<Int, TextWatcher>()
... constructor and bunch of stuff
fun updateViews(items: List<Item>, textCallback: (id, text) -> Unit) {
// Remove text watchers
views.forEach { view ->
view.removeTextWatcher(textWatchers[view.id])
}
// More views than required
while (views.size > items.size) {
val view = views.removeAt(views.size-1)
removeView(view)
}
// Less views than required
while (view.size < items.size) {
val view = createView()
view.id = View.generateViewId()
addView(view, createParams()) // Add this view to the container
views.add(view)
}
// Update the views
items.forEachIndexed { index, item ->
val editText = views[item]
// Update your edittext.
addTextWatcher(editText, item.id, textCallback)
}
}
private fun createView(): EditText {
// Create new view using inflater or just constructor and return
}
private fun createParams(): LayoutParams {
// Create layout params for the new view
}
private fun addTextWatcher(view, itemId, textCallback) {
val watcher = create text watcher where it invokes textCallback with itemId
view.addTextWatcher(watcher)
textWatchers[view.id] = watcher
}
}
Your inputs are less to identify the issue. I guess you are making some data collection application with the list of edit text.
There is a an issue when you were using the edit text in recycler list.
When you scroll down the bottom edit text in the recycler view will be filled with already filled edit text value, even though you user is not filled.
As a work around You can create some sparse array any data structure which will best suitable for you, that can map you position and value
like
mPropertyValue[] = new String [LIST_SIZE]. , assuming that position of ur list item matches with index of array.
Try updating the index with the value of text watcher
mPropertyValue[POSITION] = YOUR_EDIT_TEXT_VALUE
When you want to initialize your edit text use the value by mPropertyValue[POSITION]
You can always make sure that your edit text will be having the right value by this .
i face like this problem in my java code and that was the solution
for (int i = 0; i < list.size(); i++) {
(put her the getter and setter class) mylist = list.get(i);
//use the getter class to get values and save them or do what ever you want
}
I'm working on a small Android App. The app uses a couple Spinners. I want to be able to use the spinners, but I want the first option in the Spinner to be a hint and not selectable.
I've found a number of workarounds here on StackOverflow. But not exactly what I am looking for.
I found the following repo on Github: https://github.com/sadra/AwesomeSpinner
And it is exactly what I want to do. But, it is written in Java, whereas my app is written in Kotlin. I know they are supposed to work together, but I'm unable to get this to work. I'm pretty sure it's my inexperience that is the problem more so than the repo or a Kotlin v. Java thing.
This is my Spinner. It lives in a Fragment in it's onViewCreated().
//String array
val companyNames = resources.getStringArray(R.array.companyName_array)
var nameSpinner = binding.spinnerCustomerName
//Adapter for spinner
nameSpinner.adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_dropdown_item, companyNames)
//item selected listener for spinner
nameSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(p0: AdapterView<*>?) {
TODO("not implemented yet")
}
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
selectedCompanyName = companyNames[p2]
Log.v(LOG_TAG, "Spinner Item Selected --> $companyNames")
}
}
The AwesomeSpinner docs say to declare like this:
AwesomeSpinner nameSpinner = (AwesomeSpinner) findViewById(R.id.spinner_customerName);
In my app in Kotlin i'm doing:
var nameSpinner: AwesomeSpinner = binding.spinnerCustomerName as AwesomeSpinner
This is where my trouble starts. I cannot figure out how to get the Adapter and Listener setup correctly. I have tried every way I can think of, but I am missing something or doing something wrong.
Working on this and I cannot get the adapter right.
companyNamesAdapter.setAdapter(nameSpinner, 0)
The above is incorrect. .setAdapter remains a resolvable resource.`
This is the corrected and working code:
//String array
var nameSpinner: AwesomeSpinner = binding.spinnerCustomerName
//Adapter for spinner
var companyNamesAdapter = ArrayAdapter.createFromResource(activity,
R.array.companyname_array, android.R.layout.simple_spinner_dropdown_item)
nameSpinner.setAdapter(companyNamesAdapter, 0)
//item selected listener for spinner
nameSpinner.setOnSpinnerItemClickListener(
object:AwesomeSpinner.onSpinnerItemClickListener<String>() {
nameSpinner.setOnSpinnerItemClickListener { position: Int, itemAtPosition ->
selectedCompanyName = nameSpinner.selectedItem[position].toString()
Log.v(LOG_TAG, "Spinner Item Selected --> $companyNamesAdapter and $itemAtPosition" )
}
My XML is set up and looking OK. As are the dependencies and repositories in Gradle.
Just copied code from their manual and it works ok in Kotlin:
val spinner = findViewById<AwesomeSpinner>(R.id.spinner)
val categories = listOf("Automobile", "Ariplane")
val categoriesAdapter = ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, categories)
spinner.setAdapter(categoriesAdapter)
spinner.setOnSpinnerItemClickListener { position, itemAtPosition ->
Toast.makeText(this, "Selected on $position with $itemAtPosition", Toast.LENGTH_SHORT).show()
}
Does it helps or I miss you problem?
UPD. Looks like you mixed up adapter and spinner: companyNamesAdapter.setAdapter(nameSpinner, 0). It should be nameSpinner.setAdapter(companyNamesAdapter, 0)