I have a Question. I have an App and store every data in the Class "Model" and I wanted to save it with serialization. This is the Model:
import android.os.Parcel
import android.os.Parcelable
import java.io.Serializable
private lateinit var att : IntArray
private lateinit var ess :String
private lateinit var skills : MutableList<IntArray>
private lateinit var attSkill : MutableList<IntArray>
class Model : Serializable {
init {
att = IntArray(9) { 1 }
ess = "6"
}
fun getAttributes() : IntArray{
return att
}
fun setAttributes(position : Int, value : Int){
att[position] = value
}
}
And I Save it with:
val fos = context.openFileOutput(filename, Context.MODE_PRIVATE)
val os = ObjectOutputStream(fos)
this.saveStuff() //[Just write Attributes to model here]
var newModel = controller.getModel()
os.writeObject(newModel)
and Load it with:
val fis = context.openFileInput(filename)
val input = ObjectInputStream(fis)
val model = input.readObject() as Model
this.loadStuff(model)
input.close()
Now I get this: Lets say I save values 1 2 3 4 with filename "test1" and after that I save values 4 3 2 1 in "test2". After that I load "test1" and get 4 3 2 1. In the matter of fact I just get the values my app has right in that moment of loading the data. Checked it in load function with Log.d().
After restarting the App if I load "test1" or "test2" I will get default values.
Am I missing something? Or is it just an example why people actually use Json for such things.
att, ess etc. are no members of Model, snd therefore are not serialized to disk. Pull them into the class definition to fix that:
class Model : Serializable {
private lateinit var att : IntArray
private lateinit var ess :String
private lateinit var skills : MutableList<IntArray>
private lateinit var attSkill : MutableList<IntArray>
init {
att = IntArray(9) { 1 }
ess = "6"
}
fun getAttributes() : IntArray{
return att
}
fun setAttributes(position : Int, value : Int){
att[position] = value
}
}
You will need to provide (de-)serialization logic for these attributes. Kotlinās #Parcelize annotation might be of interest here.
Related
val data = FirebaseDatabase.getInstance()
val refB = data.getReference("branches")
refB.addValueEventListener(object : ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
for(n in snapshot.children){
val branch = n.getValue(Branch::class.java)
listB.add(branch!!)
}
}
override fun onCancelled(error: DatabaseError) {
}
})
my class Branch
class Branch(var id: String?, var nStudents: String, var nTeachers: String, var name:String) {
}
I can't read the data one sentence means Object,
But I can read the data each value by itself.
I see a couple of problems with your custom class:
As Frank pointed out in a comment, nStudents and nTeachers won't map correctly due to case sensitivity.
It seems like your class has no empty constructor (or default arguments) which is required for the database to properly deserialize the data, as detailed in the Firebase docs.
The data types don't seem to match: You've declared nstudents and nteachers as String, but I see it as numbers in your database, so you should probably use an Int or Long.
With all that said, your updated class should look like this:
class Branch(
var id: String? = null,
var nstudents: Int = 0,
var nteachers: Int = 0,
var name: String = ""
) {
// ...
}
And if the only purpose of this class is to hold data, you can turn it into a data class:
data class Branch(
var id: String? = null,
var nstudents: Int = 0,
var nteachers: Int = 0,
var name: String = ""
)
i'm trying to do an application that save/get data from realm. My problem is when I declare a realm object with a custom field variable. When I try to run the application the IDE give me this error :
error: Field "launch" of type "com.example.spacex.models.launch.LaunchModel" is not supported.
public final class NextLaunchConfiguration extends io.realm.RealmObject {
this is my custom Realm database class:
`class RealmDatabase(context: Context, selectedConfiguration: Int) {
private var realm: Realm
init {
Realm.init(context)
realm = Realm.getInstance(setUpConfiguration(selectedConfiguration))
realm.beginTransaction()
}
private fun setUpConfiguration(selectedConfiguration: Int) : RealmConfiguration {
return RealmConfiguration.Builder()
.name(getConfiguration(selectedConfiguration)).build()
}
private fun getConfiguration(selectedConfiguration: Int) : String{
when(selectedConfiguration){
1 -> return Constants.NEXT_LAUNCH_REALM
}
return ""
}
//save and get data for next Launch
fun saveNextLaunch(launch: LaunchModel) {
val saveLaunch = realm.createObject(NextLaunchConfiguration::class.java)
saveLaunch.launch.let { launch }
realm.commitTransaction()
}
fun getNextLaunch() : LaunchModel {
val data = realm.where(NextLaunchConfiguration::class.java).findAll()
return data[0]?.launch!!
}
fun closeDatabase() {
realm.close()
}
}`
this is the realmObject :
`#RealmClass(embedded = true)
class NextLaunchRealmObject : RealmObject() {
var launch: LaunchModel?= null
}`
and this is my LaunchModel:
class LaunchModel(
var details: String,
var id: String,
var isTentative: Boolean,
var launchDateLocal: Date,
var launchDateUnix: Date,
var launchDateUtc: Date,
var launchSite: LaunchSiteModel,
var launchSuccess: Boolean,
var launchYear: String,
var links: LaunchLinksModel,
var missionId: List<String>,
var missionName: String,
var rocket: LaunchRocketModel,
var staticFireDateUnix: Date,
var staticFireDateUtc: Date,
var telemetry: LaunchTelemetryModel,
var tentativeMaxPrecision: String,
var upComing: Boolean,
var ships : List<ShipModel>,
)
I tried to implement custom embedded object like in the guide ok realm but it doesn't work and I can't change the model because I'm using it for retrofit api call.
I am working in app with two languages
in autocomplatetextview i want to change values according to the language of device
i try this code
var EGP = getString(R.string.egyptian_pound_egp)
var USD = getString(R.string.american_dollar_usd)
var SAR = getString(R.string.Saudia_Ryal)
var KWD = getString(R.string.Kuwaiti_Dinar)
and full code of MainActivity
package com.example.currency
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Adapter
import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import android.widget.Button
import androidx.annotation.StringRes
import androidx.core.widget.addTextChangedListener
import com.google.android.material.internal.ContextUtils.getActivity
import com.google.android.material.textfield.TextInputEditText
class MainActivity : AppCompatActivity() {
var EGP = getString(R.string.egyptian_pound_egp)
var USD = getString(R.string.american_dollar_usd)
var SAR = getString(R.string.Saudia_Ryal)
var KWD = getString(R.string.Kuwaiti_Dinar)
lateinit var convertButton: Button
lateinit var amount: TextInputEditText
lateinit var result: TextInputEditText
lateinit var from: AutoCompleteTextView
lateinit var to: AutoCompleteTextView
val listValue = mapOf(
USD to 0.052356,
EGP to 1.0,
SAR to 0.197040,
KWD to 0.0166838
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initalizeViews()
populateMenu()
convertButton.setOnClickListener {
calcResault()
}
amount.addTextChangedListener {
calcResault()
}
}
private fun initalizeViews() {
convertButton = findViewById(R.id.button)
amount = findViewById(R.id.AmountTIET)
result = findViewById(R.id.ResultTIET)
from = findViewById(R.id.FromACTV)
to = findViewById(R.id.ToACTV)
}
private fun populateMenu() {
val currencyList = listOf(EGP, USD, SAR, KWD)
val adapter = ArrayAdapter(this, R.layout.list_currency, currencyList)
from.setAdapter(adapter)
to.setAdapter(adapter)
}
private fun calcResault(){
if (amount.text.toString().isNotEmpty()) {
result.setText(
String.format(
"%.2f", listValue.get(to.text.toString())!!.times(
amount.text.toString().toDouble()
.div(listValue.get(from.text.toString())!!)
)
)
)
} else {
amount.setError(getString(R.string.amount_required))
}
}
}
after testing some codes , i found that getString(R.string.xxx) the reason of the crashing
when change getString(R.string.xxx) with string value the app opening with no problem
but i want to change values according to the language of device
getString requires your Activity to have a Context, and at construction time it doesn't have one. So when you define those top-level variables that are initialised at construction time, your getString calls fail. The error log will tell you this, that you're trying to do something with a null Context or similar.
The context shows up somewhere around onCreate, so if you can guarantee those values won't be used until the Activity is CREATED (i.e. you won't be reading them until onCreate or later) then you could use a lazy delegate. That only initialises them when they're first read - so if you're reading them when the Activity has its Context, the getString call works fine!
val EGP = by lazy { getString(R.string.egyptian_pound_egp) }
val USD = by lazy { getString(R.string.american_dollar_usd) }
val SAR = by lazy { getString(R.string.Saudia_Ryal) }
val KWD = by lazy { getString(R.string.Kuwaiti_Dinar) }
But the problem here is you're not first reading these in onCreate or later - it happens in the next line where you build a Map using those values, which is another top-level variable that's initialised at construction. So you don't get the benefit of the lazy because it's called too early.
You can fix this by making that map lazy too:
val listValue by lazy { mapOf(
USD to 0.052356,
EGP to 1.0,
SAR to 0.197040,
KWD to 0.0166838
)}
Now listValue won't be initialised until it's read either! So it won't try to read those other values until it's actually accessed - so same deal, as long as listValue isn't read by something before onCreate, it should be fine.
This is the kind of thing you have to watch out for with lazy delegates in Android lifecycle components like Activity and Fragment, or anywhere you need lazy initialisation really. Make sure it's not being read too early by something else, and make those lazy too if appropriate.
Using lazy delegates requires your variables to be vals though - if you need to be able to change them, make them lateinit instead and initialise them manually as soon as you can. You could keep listValue as a lazy if you want, just make sure the lateinit vars it initialises from are assigned before it's accessed
Make those be either lateinit var or use by lazy {} to defer initialization. You cannot call getString() until after super.onCreate() has been called in your onCreate() function.
Try this
(CAUTION: You values will be re-initialized every time onCreate() method called)
lateinit var EGP: String
lateinit var USD: String
lateinit var listValues: Map<String, Double>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
EGP = getString(R.string.egyptian_pound_egp)
USD = getString(R.string.american_dollar_usd)
listValues = mapOf(
USD to 0.052356,
EGP to 1.0
)
//Rest of your code
}
Could someone give me a little pointer on data classes. I'm finding a very steep learning curve with Kotlin, but I'm getting there slowly.
I have a data class of:
data class newGame(
val gamename: String,
val gamedate: String,
val players: List<Player>
) {
data class Player(
val player: String,
val player_id: Int,
val score: Int,
val points: Int
)
}
I can create an instance of the outer (newGame) class, but I'm struggling to get to grips with how I add Players. I thought I could do something like:
var gm: newGame
gm.gamename = GamesList.text.toString()
val p = newGame.Player
p.player = Player1.text.toString()
gm.players.add p
But Android Studio says I need a companion object and I'm not sure how that needs to look
The interface looks like:
interface CreateGame {
#POST("new")
fun addNewGame(
#Query("operation") operation: String,
#Body() newGame: newGame
): Call<gameEvent>
companion object {
fun create(): CreateGame {
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BaseURL)
.build()
return retrofit.create(CreateGame::class.java)
}
}
I get how the companion object create() creates the instance of the game, but I can't figure out what I need to write so that I can add players to the inner class.
Could someone give me a pointer please?
I can create an instance of the outer (newGame) class, but I'm
struggling to get to grips with how I add Players. I thought I could
do something like:
Well, first understood the terms val and var. The property gamename is val which means its read-only, to write it you have to make it as var, same goes to all other properties. and players should be mutableListOf or ArrayList<> type so you can modify it later. List is immutable, which means you can't add after initialization.
data class newGame(
var gamename: String,
var gamedate: String,
var players: ArrayList<Player>
)
data class Player(
var player: String,
var player_id: Int,
var score: Int,
var points: Int
)
Now In your case, you need to add a secondary constructor OR initialize all properties while declaring as below
data class newGame(
var gamename: String? = null,
var gamedate: String? = null,
var players: ArrayList<Player> = ArrayList()
)
data class Player(
var player: String?=null,
var player_id: Int?=null,
var score: Int?=null,
var points: Int?=0
)
In main class
var gm: newGame = newGame()
gm.gamename = "GamesList.text.toString()"
val p = Player()
p.player = "Player1.text.toString()"
gm.players.add(p)
There is a difference between nested class and inner class.
A nested class cannot do the same thing as an inner class. An inner class has an implicit reference to its outer class instance. A nested class doesn't.
What you have is a nested class and I don't think your use case requires this structure. you can simply move the Player class outside the newGame class. and then you can create objects of newGame as
// Create Player objects by passing appropriate values to the constructor
val playerA = Player(player = "A", player_id = 1, score = 50, points = 50)
val playerB = Player(player = "B", player_id = 2, score = 60, points = 80)
// Create a newGame
val newGame = newGame(gamename = "MyGameName", gamedate = "GameDate", players = listOf(playerA, playerB))
This question already has an answer here:
Kotlin - How can we access private property with getter and setter? Is access methods are calling internally? [duplicate]
(1 answer)
Closed 2 years ago.
So, im trying to develop an Android app with Kotlin as an Pen and Paper RPG companion. Right now I want to make a mob class like
class Mob(name: String, health: Int, armor: Int) {
private val name: String
get() = field
private var health: Int = 0
get() = field
set(value) {
field = value
}
private val armor: Int
get() = field
}
In another activity I want to display this information like
class MeinMonster : AppCompatActivity() {
private lateinit var monster: Mob
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_mein_monster)
monster = Mob(
intent.getStringExtra("Name"),
intent.getIntExtra("Health", 20),
intent.getIntExtra("Armor", 0)
)
print()
}
private fun print() {
try {
tvName.text = monster.name
tvHealth.text = "LeP: ${monster.health}"
tvArmor.text = "RS: ${monster.armor}"
} catch (ex:Exception) {
val goBack = Intent(this, MainActivity::class.java)
startActivity(goBack)
}
}
}
Android studio is constantly telling me Cannot access 'name': It is private in 'Mob', though. I thought that's what I got the get() for?
Maybe someone with more Kotlin experience can help. Thank you in advance.
you can try to change your class for data class like these:
data class Mob(val name: String, var health: Int, val armor: Int)
When you use "val" your variable will be to get, when you use "var" your variable will be to get and set.