How to remove all items from ArrayList in kotlin - android

I have an array list in kotlin and I want to remove all item from it, leave it as an empty array to start adding new dynamic data. i tried ArrayList.remove(index) arrayList.drop(index) but none works,
The Declaration:
var fromAutoCompleteArray: List<String> = ArrayList()
here is how I try it :
for (item in fromAutoCompleteArray){
fromAutoCompleteArray.remove(0)
}
I'm using the addTextChangedListener to remove old data and add new data based on user's input:
private fun settingToAutoComplete() {
val toAutoCompleteTextView: AutoCompleteTextView =
findViewById<AutoCompleteTextView>(R.id.toAutoCompleteText)
toAutoCompleteTextView.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun afterTextChanged(s: Editable?) {
doLocationSearch(toAutoCompleteTextView.text.toString(), 2)
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
toAutoCompleteTextView.postDelayed({
toAutoCompleteTextView.showDropDown()
}, 10)
}
})
val adapter = ArrayAdapter(this, android.R.layout.select_dialog_item, toAutoCompleteArray)
toAutoCompleteTextView.setAdapter(adapter)
toAutoCompleteTextView.postDelayed({
toAutoCompleteTextView.setText("")
toAutoCompleteTextView.showDropDown()
}, 10)
}
And here is the function that adds the data :
private fun doLocationSearch(keyword: String, fromTo: Number) {
val baseURL = "api.tomtom.com"
val versionNumber = 2
val apiKey = "******************"
val url =
"https://$baseURL/search/$versionNumber/search/$keyword.json?key=$apiKey"
val client = OkHttpClient()
val request = Request.Builder().url(url).build()
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: okhttp3.Response) {
val body = response.body?.string()
println("new response is : $body")
val gson = GsonBuilder().create()
val theFeed = gson.fromJson(body, TheFeed::class.java)
if (theFeed.results != null) {
for (item in theFeed.results) {
println("result address ${item.address.freeformAddress} ")
if (fromTo == 1) {
fromAutoCompleteArray = fromAutoCompleteArray + item.address.freeformAddress
println(fromAutoCompleteArray.size)
} else {
toAutoCompleteArray = toAutoCompleteArray + item.address.freeformAddress
}
}
} else {
println("No Locations found")
}
}
override fun onFailure(call: Call, e: IOException) {
println("Failed to get the data!!")
}
})
}
and as you see the line println(fromAutoCompleteArray.size) shows me if it's deleted or not, and it is always encreasing.
Also, tried to use clear() without a loop and none works:
fromAutoCompleteArray.clear()

The List type in Kotlin is not mutable. If you want to cause your list to change, you need to declare it as a MutableList.
I would suggest changing this line:
var fromAutoCompleteArray: List<String> = ArrayList()
To this:
val fromAutoCompleteArray: MutableList<String> = mutableListOf()
And then you should be able to call any of these:
fromAutoCompleteArray.clear() // <--- Removes all elements
fromAutoCompleteArray.removeAt(0) // <--- Removes the first element
I also recommend mutableListOf() over instantiating an ArrayList yourself. Kotlin has sensible defaults and it is a bit easier to read. It will end up doing the same thing either way for the most part.
It is also preferable to use val over var, whenever possible.
Update: Vals not vars, thanks for spotting that Alexey

i dont know how how you declare arraylist but this can be done as following
var arrayone: ArrayList<String> = arrayListOf("one","two","three")
val arraytwo = arrayone.drop(2)
for (item in arraytwo) {
println(item) // now prints all except the first one...
}
in your case try this
val arraytwo = fromAutoCompleteArray.toMutableList().apply {
removeAt(0)
}

Related

Even if the address of the list changes, does the address value of the property remain the same?

Currently, I am making a task in Android that changes the unit value of the list according to the toggle button and shows the list with the changed value.
I am observing the list using a ViewModel and LiveData.
So i use toList() to return a new list and overwrite the old list to observe the values.
However, the screen is not updated even though it has returned a new list.
I've tried debugging and I'm getting some incomprehensible results.
Obviously, the address values ​​of the old list and the new list are different, but even the unit of the old list has changed.
What happened?
Even if the addresses of Lists are different, do the values ​​of the old list and the new list change at the same time because the properties refer to the same place?
I'll show you the minimal code.
Fragment
// Change Unit
toggleButton.addOnButtonCheckedListener { _, checkedId, isChecked ->
if(isChecked) {
when(checkedId) {
R.id.kg -> vm.changeUnit("kg")
R.id.lb -> vm.changeUnit("lbs")
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
vm.items.observe(viewLifecycleOwner) { newList ->
adapter.submitList(newList)
}
}
WorkoutSetInfo
#Entity(
foreignKeys = [
ForeignKey(
entity = Workout::class,
parentColumns = arrayOf("workoutId"),
childColumns = arrayOf("parentWorkoutId"),
onDelete = ForeignKey.CASCADE
)
]
)
data class WorkoutSetInfo(
#PrimaryKey(autoGenerate = true)
val id: Long = 0,
val set: Int,
var weight: String = "",
var reps: String = "",
var unit: String = "kg",
val parentWorkoutId: Long = 0
)
Adapter
class DetailAdapter
: ListAdapter<WorkoutSetInfo, DetailAdapter.ViewHolder>(DetailDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ItemRoutineDetailBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(currentList[position])
}
inner class ViewHolder(val binding: ItemRoutineDetailBinding) : RecyclerView.ViewHolder(binding.root) {
private var weightTextWatcher: TextWatcher? = null
private var repTextWatcher: TextWatcher? = null
fun bind(item: WorkoutSetInfo) {
binding.set.text = item.set.toString()
binding.weight.removeTextChangedListener(weightTextWatcher)
binding.unit.text = item.unit
binding.rep.removeTextChangedListener(repTextWatcher)
weightTextWatcher = object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { }
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { }
override fun afterTextChanged(w: Editable?) {
if(!binding.weight.hasFocus())
return
item.weight = w.toString()
}
}
repTextWatcher = object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { }
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { }
override fun afterTextChanged(r: Editable?) {
if(!binding.rep.hasFocus())
return
item.reps = r.toString()
}
}
binding.apply {
weight.setTextIfDifferent(item.weight)
weight.addTextChangedListener(weightTextWatcher)
rep.setTextIfDifferent(item.reps)
rep.addTextChangedListener(repTextWatcher)
}
}
}
}
DiffUtil*
class DetailDiffCallback : DiffUtil.ItemCallback<WorkoutSetInfo>() {
override fun areItemsTheSame(
oldItem: WorkoutSetInfo,
newItem: WorkoutSetInfo
): Boolean {
return (oldItem.id == newItem.id)
}
override fun areContentsTheSame(
oldItem: WorkoutSetInfo,
newItem: WorkoutSetInfo
): Boolean {
return oldItem == newItem
}
}
ViewModel
class DetailViewModel(application: Application, title: String) : ViewModel() {
private val workoutDao = DetailDatabase.getDatabase(application)!!.workoutDao()
private val repository: WorkoutRepository = WorkoutRepository(workoutDao, title)
private val _items: MutableLiveData<List<WorkoutSetInfo>> = MutableLiveData()
val items = _items
fun changeUnit(unit: String) {
repository.changeUnit(unit)
_items.postValue(repository.getList())
}
fun addSet() {
viewModelScope.launch(Dispatchers.IO){
repository.add()
_items.postValue(repository.getList())
}
}
fun deleteSet() {
repository.delete()
_items.postValue(repository.getList())
}
fun save() {
viewModelScope.launch(Dispatchers.IO) {
repository.save()
}
}
}
Repository
class WorkoutRepository(private val workoutDao : WorkoutDao, title: String) {
private val workout = Workout(title = title)
private val setInfoList = ArrayList<WorkoutSetInfo>()
fun changeUnit(unit: String) {
setInfoList.map { setInfo ->
setInfo.unit = unit
}
}
fun add() {
val item = WorkoutSetInfo(set = setInfoList.size + 1)
setInfoList.add(item)
}
fun delete() {
if(setInfoList.size != 0)
setInfoList.removeLast()
return
}
fun save() {
val workoutId = workoutDao.insertWorkout(workout)
val newWorkoutSetInfoList = setInfoList.map { setInfo ->
setInfo.copy(parentWorkoutId = workoutId)
}
workoutDao.insertSetInfoList(newWorkoutSetInfoList)
}
fun getList() : List<WorkoutSetInfo> = setInfoList.toList()
}
You'd need to post your observer code for any help with why it's not updating.
As for the weird behaviour, setInfoList contains a few WorkoutSetInfo objects, right? Let's call them A, B and C. When you call setInfoList.toList() you're creating a new container, which holds the same references to objects A, B and C. Because it's a separate list, you can add and remove items without affecting the original list, but any changes to the objects that both share will be reflected in both lists - because they're both looking at the same thing.
So when you do setInfoList.map { setInfo -> setInfo.unit = unit } (which should be forEach really, map creates a new list you're discarding) you're modifying A, B and C. So every list you've made that contains those objects will see those changes, including your old list.
Basically if you want each list to be independent, when you modify the list you need to create new instances of the items, which means copying your WorkoutSetInfo objects to create new ones, instead of updating the current ones. If it's a data class then you can do that fairly easily (so long as you don't have nested objects that need copying themselves):
// var so we can replace it with a new list
private var setInfoList = listOf<WorkoutSetInfo>()
fun changeUnit(unit: String) {
// create a new list, copying each item with a change to the unit property
setInfoList = setInfoList.map { setInfo ->
setInfo.copy(unit = unit)
}
}
You don't need to do toList() on getList anymore, since you're just passing the current version of the list, and that list will never change (because you'll just create a new one). Meaning you don't need that function, you can just make setInfoList public - and because I changed it to listOf which creates an immutable List, it's safe to pass around because it can't be modified.
The WorkoutSetInfo objects inside that list could still be modified externally though (e.g. by changing one of the items' unit value), so instead of making a new copy when you call changeUnit, you might want to do it when you call getList instead:
class WorkoutRepository(private val workoutDao : WorkoutDao, title: String) {
private val workout = Workout(title = title)
private val setInfoList = ArrayList<WorkoutSetInfo>()
// store the current unit here
private var currentUnit = "kg"
fun changeUnit(unit: String) {
currentUnit = unit
}
// return new List
fun getList() : List<WorkoutSetInfo> = setInfoList.map { it.copy(unit = currentUnit) }
}
Now everything that calls getList gets a unique list with unique objects, so they're all separate from each other. And if you don't actually need to store the current unit value, you could pass it in to getList instead of having a changeUnit function:
fun getList(unit: String) = setInfoList.map { it.copy(unit = unit) }

Android ArrayAdapter from Class access to specific elements

I try to write an quiz app for Android in Kotlin.
I take all data from API and create data classes
Quiz
data class Quiz(
val answerIds: List<Any>,
val groupCodes: List<Any>,
val questionList: List<Question>
)
Question
data class Question(
val answers: List<Answer>,
val groupCode: String,
val hasSimilarQuestions: Boolean,
val id: Int,
val text: String
)
Answer
data class Answer(
val addsGroupCodes: List<String>,
val id: Int,
val questionId: Int,
val text: String
)
I use Volley to make http request
My problem is:
I try to display only text from answers to specific question in ListView.
I create ArrayAdapter but I can not display only text of certain answer
class MainActivity : AppCompatActivity()
{
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val url = ""
showQuiz(url);
}
private fun showQuiz(url:String)
{
val requestQueue = Volley.newRequestQueue(this)
val pytanie : TextView = findViewById(R.id.pytanie)
val jsonObjectRequest = JsonObjectRequest(Request.Method.GET, url, null, object : Response.Listener<JSONObject?>
{
override fun onResponse(response: JSONObject?) {
try
{
val jsonArray = response?.getJSONArray("questionList")
if (jsonArray != null)
{
val quiz:Quiz = Gson().fromJson(response.toString(), Quiz::class.java)
val odp = findViewById<ListView>(R.id.odpowiedzi)
for (question in quiz.questionList)
{
val arrayAdapter: ArrayAdapter<Answer> = ArrayAdapter(this#MainActivity, android.R.layout.simple_list_item_1, question.answers)
odp.adapter = arrayAdapter
}
}
} catch (e: JSONException) {
e.printStackTrace()
}
}
},
object : Response.ErrorListener
{
override fun onErrorResponse(error: VolleyError)
{
error.printStackTrace()
Log.d("JsonObjectErr",error.toString());
}
}
)
requestQueue.add(jsonObjectRequest)
}
}
What about access to other elements in ArrayAdapter, because I need to remember what user choose.
ArrayAdapter shows value from toString() of an item.
So override toString() in Answer and return only value you want to show.

Trouble Using Edit Search Function in Recycler View with Cards

I am trying to search through a recycler view with cards by allowing a user to search. When the user searches, the cards should "reorganize" to show according to the characters entered by the user. I have tried to do this but am having issues doing this. Any assistance is appreciated.
MainActivity.kt
class MainActivity : AppCompatActivity(), BottomSheetRecyclerViewAdapter.ListTappedListener {
private var customAdapter: CustomAdapter? = null
private var arrayListModel = ArrayList<Model>()
private lateinit var bottomSheetBehavior: CustomBottomSheetBehavior<ConstraintLayout>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val modelList = readFromAsset()
val adapterList = CustomAdapter(modelList, this)
customAdapter = CustomAdapter(arrayListModel, this#MainActivity)
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetLayout) as CustomBottomSheetBehavior
recyclerView.adapter = adapterList
recyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
et_search.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) {
if (!s.isNullOrEmpty()) {
val searchList = ArrayList<Model>()
for (i in arrayListModel.indices) {
if (arrayListModel[i].name.toLowerCase().contains(s)) {
searchList.add(arrayListModel[i])
}
}
try {
customAdapter?.notifyDataSetChanged()
recyclerView.adapter = CustomAdapter(searchList, this#MainActivity)
} catch (e: Exception) {
e.printStackTrace()
}
} else {
customAdapter?.notifyDataSetChanged()
recyclerView.adapter = customAdapter
}
}
})
}
override fun onClickList(text: String) {
}
private fun readFromAsset(): List<Model> {
val modeList = mutableListOf<Model>()
val bufferReader = application.assets.open("android_version.json").bufferedReader()
val json_string = bufferReader.use {
it.readText()
}
val jsonArray = JSONArray(json_string);
for (i in 0..jsonArray.length() - 1) {
val jsonObject: JSONObject = jsonArray.getJSONObject(i)
val model = Model(jsonObject.getString("name"), jsonObject.getString("version"))
modeList.add(model)
}
return modeList
}
}
I might found your problem. Here you getting data val modelList = readFromAsset() but you are never assigning data to arrayListModel that your problem.
Assign the data to arrayListModel
val modelList = readFromAsset()
arrayListModel=modelList
Here's a clean approach you might want to consider/try:
Make your adapter implement the filterable interface.
Provide your own Filter object in which you implement your filtering logic (asynch).
You might as well use a SearchView instead of using onTextChange on an EditText.
So: onTextChange(newText) => call adapter.getFilter().filter(newText) => filtering happens in background (filter method performFiltering is called) => when filtered list ready (filter method publishResults is called), you push it to your adapter and notifyDataSetChanged.
Hope this helps.
Here's a clean example on how to implement this:
Example

Sorting data from json in Kotlin

I have data from json file which I display in recyclerview in my app. I'm trying to sort this data by year. That's how my code looks:
In MainActivity.kt everythings happend in fetchJson() function
private fun fetchJson(jsonUrl: String) {
Log.d(TAG, "Attempting to fetch json")
val request = okhttp3.Request.Builder().url(jsonUrl).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object: Callback {
override fun onFailure(call: Call, e: IOException) {
Log.d(TAG, "Failed to execute request")
}
override fun onResponse(call: Call, response: Response) {
val body = response.body()?.string()
Log.d(TAG, "$body")
val gson = GsonBuilder().create()
val homeFeed = gson.fromJson(body, HomeFeed::class.java)
homeFeed.standups.sortedWith(compareBy({it.year}))
runOnUiThread {
rv.adapter = Adapter(homeFeed)
}
}
})
}
fun <T> compareBy(vararg selectors: (T) -> Comparable<*>?): Comparator<T> {
return Comparator<T> { a, b -> compareValuesBy(a, b, *selectors) }
}
class HomeFeed is here:
class HomeFeed(val standups: List<StandUps>)
and data class StandUps:
data class StandUps(
val artist: String,
val title: String,
val year: String,
val poster: String,
val description: String,
val netflix_link: String,
val imdb_rate: String,
val imdb_link: String,
val duration_min: String
)
It doesn't shows any errors or warnings, it just doesn't do anything. How could I achieve this?
You have to first store the sorted list in another variable and then use that variable to pass it to your adapter
val homeFeed = gson.fromJson(body, HomeFeed::class.java)
val sortedHomeFeed = homeFeed.standups.sortedWith(compareBy({it.year}))
runOnUiThread {
rv.adapter = Adapter(sortedHomeFeed)
}
The reason for this is, changes are not made to the original list following the concepts of immutability.
Kotlin gives you easy sorting. Jus like below
make a temp object (i.e)., tempFilterData here
val standUps = tempFilterData?.sortedWith(compareBy({ it.Year }))
Now you can get the sorted data based on YEAR
If you want to sort your list ascending by a year you can do this:
val sortedStandUps = homeFeed.standups.sortedBy { it.year }
If you want to sort list descending do this:
val sortedStandUps = homeFeed.standups.sortedByDescending { it.year }

Trouble with "For-Loop range must have an 'iterator()' method" and JSON issues

I am teaching myself Kotlin and android dev. So, i'm sure most of my issue is lack of knowledge, but i've been hung up on this part for day or two. I think my issue is partly my JSON query, and mostly my rookieness.
In my for loop below, I'm getting the following error from the IDE "For-Loop range must have an 'iterator()' method". This is in regards to 'cycloneList' in: for(stormInfo in cycloneList)
I've linked my "dummy" JSON data I'm using can be found here: https://api.myjson.com/bins/19uurt to save a bit of space here in the question.
Problem code
`var cycloneList = response?.body()?.currenthurricane?.stormInfo?.get(0)
if (cycloneList != null) {
for (stormInfo in cycloneList) { <<--Problem
val newCyclone = "Name: ${cycloneList.stormName}"
cycloneStrings.add(newCyclone)
}
}`
FULL CODE
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//creates a new CycloneRetriever object from CycloneHelp.kt
var retriever = CycloneRetriever()
val callback = object : Callback<Cyclones> {
override fun onResponse(call: Call<Cyclones>?, response: Response<Cyclones>?) {
println("Got a response!")
var cycloneList = response?.body()?.currenthurricane?.stormInfo?.get(0)
var cycloneStrings = mutableListOf<String>()
if (cycloneList != null) {
for (stormInfo in cycloneList) { //TODO Figure this out!!
val newCyclone = "Name: ${cycloneList.stormName}"
cycloneStrings.add(newCyclone)
}
}
var layoutMovieListView = movieListView
var movieListAdapter = ArrayAdapter(this#MainActivity, android.R.layout.simple_list_item_1,
cycloneStrings)
layoutMovieListView.adapter = movieListAdapter
}
override fun onFailure(call: Call<Cyclones>?, t: Throwable?) {
println("The thing, it failed!")
}
}
retriever.getCyclones(callback)
}
I'm using Retrofit to access/handle JSON data
Retrofit JSON code
interface WeatherWunderGroundAPI {
#GET("bins/19uurt")
fun getCyclones() : Call<Cyclones>
}
class Cyclones(val currenthurricane: CurrentHurricane)
class CurrentHurricane(val stormInfo: List<StormInfo>)
class StormInfo(val stormName: String)
class CycloneRetriever {
val service : WeatherWunderGroundAPI
init {
val = retrofitCyclone
Retrofit.Builder().baseUrl("https://api.myjson.com/")
.addConverterFactory(GsonConverterFactory.create()).build()
service = retrofitCyclone.create(WeatherWunderGroundAPI::class.java)
}
fun getCyclones(callback: Callback<Cyclones>) {
val call = service.getCyclones()
call.enqueue(callback)
}
}
Your stormInfo is a List which provides the mandatory iterator() and this is what you want to iterate over:
var cycloneList = response?.body()?.currenthurricane?.stormInfo ?: emptyList()
for (stormInfo in cycloneList) { }
Hope it helps...

Categories

Resources