Can't add item in MutableList inside a LiveData Observer - android-studio

I am trying to get multiple data from viwmodel via livedata ovserver with help of a for loop.
I am successfully get all data one by one and show them in Toast massage. But the problem is, I can't add those data inside a mutable list.
I can add item in this list from onCreate method. But I can't add any item from inside of observer.
Edited:
private val vocabCount: MutableList<Long> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
for (i in 1..5){
folderListViewModel.getVocabCount("$i").observe(this,{it ->
vocabCount.add(it)
})
}
}
every time getVocabCount("$i") function take a string peramiter and return a long value. Which I want to add in my Mutable list.

use mutableListOf() instead of ArrayList() like this
val vocabCount: MutableList<Long> = mutableListOf()
then instead of "add" use "="
vocabCount = it

Related

Unable to display the age of an object that was passed to a kotlin function

I'm just starting to learn kotlin and ran into a problem:
I have a Person class that has two fields
-age (Int data type)
-name (data type String)
there is also a oldUp function where I pass a Person object and increment the object's age field by 10.
Before the end of the program ** I want to display the age of the object that was passed to oldUp **
However, age is not shown.
my code:
class Person(var name: String, var age: Int){
}
fun growOld(human: Person){
human.age+=10
}
fun main(args: Array<String>) {
var human = Person("Kitty",6)
growOld(human)
println(human)
}
If you want to print the age, you can just write: println(human.age).
In your example it might be cleaner to add the growOld method to your class so you can call it on the object. For example:
class Person(var name: String, var age: Int){
fun growOld() {
this.age += 10
}
}
fun main() {
var human = Person("Kitty", 6)
println(human.age) // prints 6
human.growOld()
println(human.age) // prints 16
println(human.name) // prints Kitty
}
The problem is you're trying to print the human object itself. Under the hood, this calls its toString() method - every class has one of these, because it's defined on the type all classes derive from. If you don't override it and provide a nice way to "pretty print" your object, it'll use the default implementation, which is basically a reference to the object in memory.
A lot of classes you use have a nice toString() implementation, e.g. if you print a List you get ["something", "that", "looks", "like", "this"]. But that behaviour needed to be coded in - and you need to do that for your Person class too!
So you can override the default implementation like this:
override fun toString(): String {
// return a String here
}
override means you're taking an existing function and writing your own version of it to use instead - if this doesn't match an existing function you can override, you'll get an error. You'll also get an error if you don't use the override keyword for a function that looks exactly like an existing one in a supertype - it's just to make sure you don't accidentally do the wrong thing. In IntelliJ you can do Ctrl+O to override existing functions if you like.
So you could do something like this:
// inside your Person class
override fun toString(): String {
return "Name: $name, age: $age"
}
and then when you use it in a print statement, or in a string (like "Details: $person" or val details = "Details: " + person) it will call that toString() method and get the string you produced.
Another way to approach this is to use a data class:
data class Person(var name: String, var age: Int)
A data class is a special kind of class where all your "data" goes in the constructor (as properties, either val or var), and then you get some boilerplate stuff for free which uses those properties (and only those properties). Things like an equals() and hashCode() implementation that uses that data - and the relevant thing here, it gives you a toString() implementation that pretty prints name and age. Try it out!
Data classes can be really handy for simple data objects like you have here - but in normal classes, overriding toString() yourself is the general way of doing things. And you can still override a data class's toString if you want - sometimes you might want a more complex representation, or nice formatting, or you might want to only include some properties and ignore others. You're in control of how it prints itself!
And if you just want to print the age property, or print anything at all using the data in your object, then you just need to do what Robin's answer says. You don't need a toString() implementation at all for that (and since this is how you usually use objects, often you won't need to write a toString for your own classes at all)

Show Data from Arraylist in list view in Kotlin and Android Studio

In my code Im showing the project name in the list view. But I want to show the name of all added Contacts.
showAllContactsBtn.setOnClickListener(){
val arrayAdapter: ArrayAdapter<Contact> = ArrayAdapter(
this,android.R.layout.simple_list_item_1,listOfAllNames
)
listNames.adapter = arrayAdapter
listNames.setOnItemClickListener { adapterview, view, x, y ->
Toast.makeText(this, "Contact picked" + listofAllnames[x].name, Toast.LENGTH_LONG).show()
}
How do I show the names like in Toast "list of All names . name"?
An ArrayAdapter just calls toString() on the items you pass in, and displays those. So you could just pass in all the name values instead:
val arrayAdapter: ArrayAdapter<Contact> = ArrayAdapter(
this, android.R.layout.simple_list_item_1, listOfAllNames.map { it.name }
)
If you need to keep the original objects in the ArrayAdapter for whatever reason, you could override the toString() method in the objects' class so it just returns the name. But since you're just looking up an index in the original data set here, just throwing the list of labels into the ArrayAdapter constructor is probably fine!

Dice roller app, formatting attribute id calls using Kotlin

I'm taking a kotlin basics course from the android devs website and we have to create an app that allows you to roll a dice and display an image with the number that rolled.
I can make a condition for each possible roll number, but i feel like that's dumb and with formatted reference id calls it should be done way better but I don't know how to accomplish that in kotlin using Android Studio. Any helpers?
package com.example.diceroller
import android.os.Bundle
import android.widget.Button
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
/**
* Calls the onCreate() method to initiate the app in its MainActivity.
* setOnClickListener to look for activity in the window (button presses/taps).
*/
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.button)
rollButton.setOnClickListener { rollDice() }
/* Toast.makeText(this,"Dice Rolled!", Toast.LENGTH_SHORT).show() | Shows alert (toast) at bottom of the screen. */
}
/*
* Initiates a 'Dice' instance with 6 sides.
* calls the roll() method to call rng between 1 and numSides.
*
*/
private fun rollDice() {
val dice = Dice(6)
val diceRoll = dice.roll()
val diceImage: ImageView = findViewById(R.id.imageView)
if (diceRoll == 1){ diceImage.setImageResource(R.drawable.dice_1) }
else if (diceRoll == 2){ diceImage.setImageResource(R.drawable.dice_2) }
//Instead of doing it the "dumb way" I want to call the id depending on diceRoll value.
}
}
class Dice(private val numSides: Int) {
fun roll(): Int {
return (1..numSides).random()
}
}
If you're asking how to add 1 or whatever to "R.drawable.dice_" and then turn that string into a resource ID, then you can follow the accepted answer here: https://stackoverflow.com/a/2414165/13598222
Personally I wouldn't recommend that - it's brittle since the compiler can't connect what you're doing to a specific resource. So it can't warn you if you change the resource name (so your code doesn't match anymore), and things like Proguard can end up stripping out "unused" resources (because it can't tell you're using them) and you have to manually fix that.
Probably the most standard way to do what you're doing is to use a when clause, and just return the drawable ID from that:
val drawableId = when(diceRoll) {
1 -> R.drawable.dice_1
2 -> R.drawable.dice_2
3 -> R.drawable.dice_3
4 -> R.drawable.dice_4
5 -> R.drawable.dice_5
else -> R.drawable.dice_6
}
diceImage.setImageResource(drawableId)
Sure it's a little repetitive, but it's a standard pattern, it's very easy to understand and work with, and you're explicitly handling all the cases by adding the (required) else clause - you could also handle 6 explicitly and make the else throw an out-of-range message, in case your roll function somehow sends an invalid value (which it could, since you're hardcoding six values here, but your Dice class can have an arbitrary number!)
Another way you could do it is to have a lookup, like a Map:
private val diceDrawables = mapOf {
1 to R.drawable.dice_1,
2 to R.drawable.dice_2,
3 to R.drawable.dice_3,
4 to R.drawable.dice_4,
5 to R.drawable.dice_5,
6 to R.drawable.dice_6
}
and then your image setting code can be nice and short:
diceImage.setImageResource(diceDrawables[diceRoll] ?: throw Exception("No image for roll: $diceRoll))
This isn't the "dumb" way to do things, it's neat and tidy and avoids repetition (e.g. repeating the setImageResource call). Getting clever with your code logic can cut down a few lines, but it can also make things much more complex and fragile, and just harder to work with. Sometimes that kind of thing is the right call, but in this situation I think this is probably the best compromise. Your call though!
Create a list using those resource ids.
private fun rollDice() {
val diceImages = listOf(R.drawable.dice_1, R.drawable.dice_2, ...)
val diceImage: ImageView = findViewById(R.id.imageView)
discImage.setImageResource(discImages.random())
}

How do I add map markers to a separate class file?

In android studio I've created a map with hundreds of markers on it. I want to separate these into individual classes and put them in a separate package, so that there isn't one massive list of markers in my main code. Is there a way of doing this? I'm using Kotlin.
so what I think you are trying to say is.
There is an Activity let's say MainActivity and it has maps in it and has let's say some 200 markers on it.
and all are individually initialized and assigned and you want to club them all together so that you'll be able to use them just by searching for one.
if that's the case, what I would suggest is.
make a separate Data Class that stores Marker and other data related to it.
data class MarkerInfo(
//marker ID
val id:Int,
//marker Data
val markerData:Marker,
//other Data
var otherData:String
)
now coming to storing and accessing data.
class MainActivity(){
//at the top level, inside Activity
// This will create an empty list of Marker Info
var markerData = mutableListOf<MarkerInfo>()
//Now take any function, let's say x
private fun x(){
//Mark a Marker on Map and assign it to a variable.
val markerA : Marker = map.addMarker( MarkerOptions.position(somePosition))
//we assign a function with id and other relevant data
val x= markerData.size
//store data in list
markerData.add(MarkerInfo(x, markerA, "This is a very useful marker"))
}
}
//now to access that marker.
//let's say there is a function named y.
private fun y(){
//say we want to access the first marker
//there are two ways to do so.
//first method: you know some data which is already in there let's say we know id
for(i in markerData){
if(i.id == 1){
Log.d("TAG", i.toString())
}
}
//second method: directly
Log.d("TAG",markerData[0].toString())
}

Java - Adding List<String> from parameter to existing list

I want to be able to add new entries of parameter inputs to the list.
For example:
public static void theList (List<String> wholeList) {
wholeList = new ArrayList<String>();
wholeList.add("Lettuce");
wholeList.add("Bacon");
wholeList.add("Milk");
wholeList.add(wholeList); <--------- error - addAll doesn't fix it.
Above I tried ' wholeList.add(wholeList) '. What I intended to do is: Whatever additional (item (from parameter), when adding the input to run this method) item I need to add, will be added to the ' wholeList '.
As you can see, I have 3 items added to the list: Lettuce, Bacon and Milk. However, if I suddenly changed my mind, and want to add another item (via parameter), I can simply add it to the current list (wholeList).
Also, another question.
Is there a neater way to add a list of items instead of adding it one-by-one (whilst using the same list import)? Say, {"Lettuce", "Milk", "Bacon", etc}?
TY.
As I understand, addAll() is everything you need:
List<String> someList = new ArrayList<String>();
List<String> itemsToAdd = new ArrayList<String>();
itemsToAdd.add("one");
itemsToAdd.add("two");
someList.addAll(itemsToAdd);
// or use handy method which creates temporary list internally:
someList.addAll(Arrays.asList("three", "four"));
Well, your code does something very wrong.
You initialize the wholeList inside the method, and after the method is finished, it is gone (pointers in Java).
Also, you added the list on itself, so the code is probably not what you wanted to do.
you probably meant to create a new list inside the method and add all the items to the list in the parameter.
If so, you shouldn't use "new" on the list that you got from a parameter.
Actually, after reading the title of your question -
You need an existing list - it can't be with the name of the list in the parameter. Let's call it existingList.
After you get the list in the method, you shouldn't use the "new ArralyList" on it, as it will void the list from the parameter.
Your code should look like that:
public static void theList (List<String> wholeList) {
wholeList.add("Lettuce");
wholeList.add("Bacon");
wholeList.add("Milk");
existingList.add(wholeList);
The only "cleaner" way of adding the values to the list would be:
wholelist.addAll(Arrays.asList("Lettuce", "Bacon", "Milk"));
But I see the Top answer already states that. So, you could clean it up more by creating a array as a global private variable outside of the method. Also, as another answer said, you should have another seperate list that does not share the same name as the parameter list. Here is an example with libaries needed:
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
public class Example{
private List<String> globalList = new ArrayList<>();
private String[] list = {"Bacon", "Lettuce", "Milk"};
public static void theList (List<String> wholelist) {
wholelist.addAll(Arrays.asList(list));
globalList.addAll(wholeList);
}
If you wanted to use wholeList as the name for both lists, then you could change globalList above to wholelist, then:
public static void theList (List<String> wholelist) {
this.wholelist.AddAll(wholelist);
this.wholelist.addAll(Arrays.asList(list));
}
But I would avoid doing that.

Resources