I have in one activity 6 spinners and at the beginnig to 1st one it fetchs data automatically using rest api and retrofit and the for another spinner i need use value which was selected from 1st spinner and fo the 3d spinner i need get selectedItem from second spinner and so on.
i've allready (for fast result) try this in my Activity class:
private fun buildCountryDropDown(order: Int, objectName: Spinner?) {
try {
var rooms: Call<Created>? = null
var selectedObject: String? = null
val data = jsonApi.getObjects()
data?.enqueue(object : Callback<Created> {
override fun onResponse(call: Call<Created>, response: Response<Created>) {
if (response.isSuccessful) {
listOfDataObjects = response.body()!!.data.toMutableList()
println("text==========: " + response.body()?.data?.get(0)?.NameRu)
val cAdapter = CreatedAdapter(this#PlanJobActivity, android.R.layout.simple_spinner_item, listOfDataObjects as ArrayList<Data>?)
objectName?.adapter = cAdapter
}
}
override fun onFailure(call: Call<Created>?, t: Throwable?) {
t?.printStackTrace()
}
})
objectName?.onItemSelectedListener = object : OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
selectedObject = listOfDataObjects[position].tid
rooms = jsonApi.getRooms(selectedObject!!)
showToast("$selectedObject was selected!")
}
override fun onNothingSelected(parent: AdapterView<*>) {}
}
//=============================================ROOMS============================
rooms?.enqueue(object : Callback<Created> {
override fun onResponse(call: Call<Created>, response: Response<Created>) {
if (response.isSuccessful) {
listOfRooms = response.body()!!.data.toMutableList()
println("text==========: " + response.body()?.data?.get(0)?.NameRu)
val cAdapter = CreatedAdapter(this#PlanJobActivity, android.R.layout.simple_spinner_item, listOfRooms as ArrayList<Data>?)
vnp?.adapter = cAdapter
}
}
override fun onFailure(call: Call<Created>?, t: Throwable?) {
t?.printStackTrace()
}
})
vnp?.onItemSelectedListener = object : OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
selectedObject = listOfRooms[position].tid
showToast("$selectedObject was selected!")
}
override fun onNothingSelected(parent: AdapterView<*>) {}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
I know it is very very bad idea to write logic in activity but i just try, but not works. it fetch data only for first spinner but other spinners stay empty. For fetching data from rest api i'm using a retrofit and how can you see i get data from responce by enqueue method witch allows me to fetch data async. I understand that i need remaster my project to MVP but i dont know how to start?
Related
I have a spinner in my app that should be populated with a list of suppliers. The spinner originally came from some example code and works perfectly using the fixed strings in the example. However, in my case, I get my list of values (suppliers) from an API. I can see the API being called correctly and the data returned in Logcat. I want to get the data from this response and use it as the object list for my spinner.
My question is two fold. Is there a more direct way of translating the response to the adapter, and if not, how do I translate reading the response into something the spinner will use?
My intent code looks like this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".AddNewSupplier">
<TextView
android:id="#+id/lblSupplierName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="24dp"
android:text="Supplier Name"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Spinner
android:id="#+id/spinSelectSupplier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="#+id/lblSupplierName"
app:layout_constraintStart_toStartOf="#+id/txtSupplierName"
app:layout_constraintTop_toTopOf="#+id/lblSupplierName" />
</androidx.constraintlayout.widget.ConstraintLayout>
The class is:
class AddNewSupplier : AppCompatActivity() {
private lateinit var lblSupplierName: TextView
private lateinit var spinSelectSupplier: Spinner
var aSups: MutableList<String> = ArrayList()
var strSups: String = ""
var arrSuppliers = arrayOf("")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_new_supplier)
lblSupplierName = findViewById(R.id.lblSupplierName)
spinSelectSupplier = findViewById(R.id.spinSelectSupplier)
loadSupplierList()
var list_of_items = arrayOf(strSups)
if (spinSelectSupplier != null) {
val adapter = ArrayAdapter(this,
android.R.layout.simple_spinner_item, list_of_items)
spinSelectSupplier.adapter = adapter
spinSelectSupplier.onItemSelectedListener = object :
AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>,
view: View, position: Int, id: Long) {
Log.d("Seld: ", aSups[position].toString())
}
override fun onNothingSelected(parent: AdapterView<*>) {
// write code to perform some action
}
}
}
}
private fun loadSupplierList() {
AllSuppliers.create().getAllSuppliers("all").enqueue(object : Callback<SupplierList> {
override fun onFailure(call: Call<SupplierList>, t: Throwable) {
Log.d("Error :: ", t.localizedMessage!!)
t.printStackTrace()
}
override fun onResponse(
call: Call<SupplierList>,
response: Response<SupplierList>
) {
Log.d("AllSuppliers :: ", response.body().toString())
val suppliers = response.body()?.APIResult!![0]
for (i in 0..suppliers.suppliers.size - 1) {
Log.d("Each: ", suppliers.suppliers[i].supplier_name)
aSups.add(suppliers.suppliers[i].supplier_name)
strSups += ", \"" + suppliers.suppliers[i].supplier_name + "\""
}
strSups = strSups.subSequence(1, strSups.length).toString()
Log.d("StrArr:", strSups)
Log.d("ArrElem:", aSups[1].toString())
}
})
}
}
The API being called to get the suppliers returns 3 suppliers and I can see each one in the loop so I know the response has been fully returned at this point.
I have tried appending the supplier to the array aSup and this doesn't work for the spinner (the spinner is null) although I can read element 1 happily. I even tried building a string (list_of_items) giving me "supplier 1", "supplier 2" etc as this is the same as the working example that I took the code from, but I still get a a null array. Can someone explain what I'm doing wrong please?
Try to fill the spinner after call the loadSupplierList(). I mean make sure strSups is not null. Try to fill the spinner in onResponse() method.
Like this:
class AddNewSupplier : AppCompatActivity() {
private lateinit var lblSupplierName: TextView
private lateinit var spinSelectSupplier: Spinner
var aSups: MutableList<String> = ArrayList()
var strSups: String = ""
var arrSuppliers = arrayOf("")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_new_supplier)
lblSupplierName = findViewById(R.id.lblSupplierName)
spinSelectSupplier = findViewById(R.id.spinSelectSupplier)
loadSupplierList()
}
private fun loadSupplierList() {
AllSuppliers.create().getAllSuppliers("all").enqueue(object : Callback<SupplierList> {
override fun onFailure(call: Call<SupplierList>, t: Throwable) {
Log.d("Error :: ", t.localizedMessage!!)
t.printStackTrace()
}
override fun onResponse(
call: Call<SupplierList>,
response: Response<SupplierList>
) {
Log.d("AllSuppliers :: ", response.body().toString())
val suppliers = response.body()?.APIResult!![0]
for (i in 0..suppliers.suppliers.size - 1) {
Log.d("Each: ", suppliers.suppliers[i].supplier_name)
aSups.add(suppliers.suppliers[i].supplier_name)
strSups += ", \"" + suppliers.suppliers[i].supplier_name + "\""
}
strSups = strSups.subSequence(1, strSups.length).toString()
Log.d("StrArr:", strSups)
Log.d("ArrElem:", aSups[1].toString())
// call the new method here
fillSpinner()
}
})
}
private fun fillSpinner(){
var list_of_items = arrayOf(strSups)
if (spinSelectSupplier != null) {
val adapter = ArrayAdapter(this,
android.R.layout.simple_spinner_item, list_of_items)
spinSelectSupplier.adapter = adapter
spinSelectSupplier.onItemSelectedListener = object :
AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>,
view: View, position: Int, id: Long) {
Log.d("Seld: ", aSups[position].toString())
}
override fun onNothingSelected(parent: AdapterView<*>) {
// write code to perform some action
}
}
}
}
}
I am using kotlin and android 8.0, I have 2 spinners in my code, 1 of them was created using a string array from resource and another was created with an ArrayList. For the spinner that was created with an ArrayList, when clicking on the item, it does not run the OnItemSelected function and does not update the spinner.
Here is my kotlin code for the spinner
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val modules = ArrayList<Module>()
val bufferedReader = BufferedReader(InputStreamReader(resources.openRawResource(R.raw.mod_code)))
var line = bufferedReader.readLine()
while(line != null) {
val values = line.split(",")
modules.add(Module(values[0].replace("\uFEFF", "").toInt(), values[1], values[2].replace("\uFEFF", "").toDouble()))
line = bufferedReader.readLine()
}
val moduleCodeArray = ArrayList<String>()
val yearSpinner: Spinner = findViewById(R.id.year)
yearSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val item = parent?.getItemAtPosition(position).toString()
Toast.makeText(parent?.context, "Selected: $item", Toast.LENGTH_LONG).show()
moduleCodeArray.clear()
for(module in modules) {
if(module.year == position + 1) {
moduleCodeArray.add(module.moduleCode)
}
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
ArrayAdapter.createFromResource(
this,
R.array.years,
R.layout.custom_spinner
).also { adapter ->
adapter.setDropDownViewResource(R.layout.custom_spinner_dropdown)
yearSpinner.adapter = adapter
}
println(moduleCodeArray)
val moduleCodeSpinner = findViewById<Spinner>(R.id.module_code)
moduleCodeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val item = parent?.getItemAtPosition(position).toString()
Toast.makeText(parent?.context, "Selected: $item", Toast.LENGTH_LONG).show()
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
ArrayAdapter(
this,
R.layout.custom_spinner,
moduleCodeArray
).also { adapter ->
adapter.setDropDownViewResource(R.layout.custom_spinner_dropdown)
moduleCodeSpinner.adapter = adapter
}
}
}
EDIT: I have fixed the problem! In the code I provided, you can see my ArrayList being filled in an onItemSelected function, for some reason, the ArrayAdapter has to be in that function as well, so the correct code should look something like this.
val modules = ArrayList<Module>()
val bufferedReader = BufferedReader(InputStreamReader(resources.openRawResource(R.raw.mod_code)))
var line = bufferedReader.readLine()
while(line != null) {
val values = line.split(",")
modules.add(Module(values[0].replace("\uFEFF", "").toInt(), values[1], values[2].replace("\uFEFF", "").toDouble()))
line = bufferedReader.readLine()
}
val moduleCodeArray = ArrayList<String>()
val moduleCodeSpinner = findViewById<Spinner>(R.id.module_code)
val yearSpinner: Spinner = findViewById(R.id.year)
yearSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val item = parent?.getItemAtPosition(position).toString()
Toast.makeText(parent?.context, "Selected: $item", Toast.LENGTH_LONG).show()
moduleCodeArray.clear()
for(module in modules) {
if(module.year == position + 1) {
moduleCodeArray.add(module.moduleCode)
}
}
ArrayAdapter(
applicationContext,
R.layout.custom_spinner,
moduleCodeArray
).also { adapter ->
adapter.setDropDownViewResource(R.layout.custom_spinner_dropdown)
moduleCodeSpinner.adapter = adapter
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
ArrayAdapter.createFromResource(
this,
R.array.years,
R.layout.custom_spinner
).also { adapter ->
adapter.setDropDownViewResource(R.layout.custom_spinner_dropdown)
yearSpinner.adapter = adapter
}
moduleCodeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val item = parent?.getItemAtPosition(position).toString()
Toast.makeText(parent?.context, "Selected: $item", Toast.LENGTH_LONG).show()
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
Your Code Seems correct , I have used the same code provided by you and it worked correctly.
May you have error in custom_spinner or custom_spinner_dropdown !![You can share these layouts ]
This is what I have done:
spinner example
I am developing android app and I am getting error screenshot below when I have implemented network call in mainactivity.kt I want to know where I am making mistake
below my MainActivity.kt
class MainActivity : AppCompatActivity() {
private var adapter: CurrenciesAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recycler_main.layoutManager = LinearLayoutManager(this#MainActivity)
adapter = CurrenciesAdapter(this)
recycler_main.adapter = adapter
if (isInternetAvailable()) {
getUsersData()
}
}
private fun getUsersData() {
showProgressBar()
var apiInterface: CurrenciesResponse = CurrencyClient().getApiClient()!!.create(
CurrenciesResponse::class.java
)
apiInterface.getCurrencies().enqueue(object : Callback <List<CurrencyResponse>> {
override fun onResponse(
call: Call<List<CurrencyResponse>>,
response: Response<List<CurrencyResponse>>)
{
hideProgressBar()
val currencyResponse = response.body()
adapter?.list = currencyResponse!!
}
override fun onFailure(call: Call<List<CurrencyResponse>>, t: Throwable) {
hideProgressBar()
Log.e("error", t.localizedMessage)
}
})
}
}
what I have done I have changed to response type from <List to CurrencyResponse but I am still getting response below whole gist code
https://gist.github.com/kyodgorbek/d0d9b3749ac64f15b4db87874cfe13e7
Your getCurrencies() method in CurrenciesResponse.class has a return type of CurrenciesResponse whereas it should be List<CurrenciesResponse>.
You need to fix your retrofit's service interface.
I want to populate spinner with room database in kotlin but items in spinner are different. the number of items are true. exactly I want to make ArrayList of title in Category Class and show them in spinner in a fragment
#Entity(tableName = "cat_table")
class Category(val title: String,
val fulType: Int,
val SaleP: Long,
val BuyP: Long,
var created: Date = Date()
) {
#PrimaryKey(autoGenerate = true)
var id: Int = 0
}`
by this an want to show all title in ArrayList and set to spinner
private fun initData() {
val packageTypesAdapter = context?.let {ArrayAdapter<Any>(it,android.R.layout.simple_spinner_item)}
catViewModel!!.allCats.observe(viewLifecycleOwner, Observer { packageTypes ->
packageTypes?.forEach {packageTypesAdapter!!.add(it)}
})
spinner2.adapter = packageTypesAdapter
spinner2.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(adapterView: AdapterView<*>, view: View, i: Int, l: Long) {
Toast.makeText(context, "$packageTypesAdapter", Toast.LENGTH_SHORT).show()
//if (i == 0) {
// loadAllTodos()
//} else {
// val string: String = parent.getItemAtPosition(position).toString()
// loadFilteredTodos(string)
//}
}
override fun onNothingSelected(adapterView: AdapterView<*>) {
}
}
}`
if use this query
#get:Query("SELECT * FROM cat_table WHERE title ")
val allCatTitle: LiveData<List<Category>>
spinner nothing to show and below query are like this picture
#get:Query("SELECT * FROM cat_table ORDER BY created DESC")
val allCatByDate: LiveData<List<Category>>
Please check the photo
In case someone else bumps into the same issue. Here's how I did it a while ago (Kotlin).
Dao
#Query("SELECT deviceName FROM device_table WHERE deviceType = :deviceType")
fun getDevice(deviceType: String): LiveData<List<String>>
Repository
fun getDevice(deviceType: String): LiveData<List<String>> {
return deviceDao.getDevice(deviceType)
}
ViewModel
fun getDevice(deviceType: String): LiveData<List<String>> {
return repository.getDevice(deviceType)
}
And finally in my fragment:
private fun initSpinnerData() {
val spinner3 = view?.findViewById<Spinner>(R.id.spinnerRecord_devices)
if (spinner3 != null) {
val allDevices = context?.let {
ArrayAdapter<Any>(it, R.layout.spinner_text)
}
mDevicesViewModel.getDevice("Appliance")
.observe(viewLifecycleOwner, { devices ->
devices?.forEach {
allDevices?.add(it)
}
})
spinner3.adapter = allDevices
spinner3.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
Toast.makeText(requireContext(), "$allDevices", Toast.LENGTH_LONG).show()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
}
}
}
I want to fetch some json data, see in the image the green arrow:
The problem is that Android Studio doesn't let me get the data I want. It stops until a step before (I think). In my adapter class check:
holder?.view?.textWeather?.text = weatherFor.weather.toString()
Also it shows me in the emulator the red arrow, what is this?
Below is my main Activity's json method with the classes i want to fetch data for, and the associated Adapter class.
Main Activity
fun fetchJson() {
val url="https://api.openweathermap.org/data/2.5/forecast?q=Prague,CZ&appid=4cf7f6610d941a1ca7583f50e7e41ba3"
val request=Request.Builder().url(url).build()
val client= OkHttpClient()
client.newCall(request).enqueue(object :Callback {
override fun onResponse(call: Call?, response: Response?) {
val body=response?.body()?.string()
println(body)
val gson=GsonBuilder().create()
val forecastfeed=gson.fromJson(body,ForecastFeed::class.java)
runOnUiThread{
recyclerView_main.adapter=MainAdapter(forecastfeed)
}
}
override fun onFailure(call: Call?, e: IOException?) {
println("Failed to execute request")
}
})
}
class ForecastFeed(val list:List<ForecastWeatherList>) { }
class ForecastWeatherList(val weather:List<WeatherData>) { }
class WeatherData(val main:String,val icon:String) { }
Adapter
class MainAdapter(val forecastfeed: ForecastFeed): RecyclerView.Adapter<CustomViewHolder>() {
val forecastWeather = listOf<String>("First","Second")
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
val weatherFor = forecastfeed.list.get(position)
holder?.view?.textWeather?.text = weatherFor.weather.toString()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder{
//how do we even create a view
val layoutInflater =LayoutInflater.from(parent?.context)
val cellForRow=layoutInflater.inflate(R.layout.weather_row,parent,false)
return CustomViewHolder(cellForRow)
}
override fun getItemCount(): Int {
return forecastfeed.list.count()
}
}
class CustomViewHolder(val view: View):RecyclerView.ViewHolder(view) { }
You can format the data manually
holder?.view?.textWeather?.text = "weather ${weatherFor.weather.map{it.main}.joinToString(", ")}"
or use data classes
You need to overwrite WeatherData.toString() to have a hand on what's displayed.
class WeatherData(val main:String,val icon:String) {
override fun toString(): String {
return "$main $icon"
}
}
Further more you should use a RecyclerView with a ViewHolder to handle properties one-by-one and enable more complex layouts. If needed.