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.
Related
I am learning Kotlin by trying to build a small app that find and and remember last connected BLE device. To recognize the last connected device I decide to save its MAC address using shared preferences (is that the best way to do that is also a question). I use a tutorial online and it worked well (I didn't remember the page) but today when I open the project to continue the job it gives me error - unresolved reference getSharedPreferences. My question is what is the problem - I get lost :) Here is the class where I have the error row 23.
import android.content.Context
import android.content.SharedPreferences
interface PreferencesFunctions {
fun setDeviceMAC(deviceMAC: String)
fun getDeviceMAC(): String
fun setLastConnectionTime(lastConnectionTime: String)
fun getLastConnectionTime(): String
fun clearPrefs()
}
class PreferenceManager(context: ScanResultAdapter.ViewHolder) : PreferencesFunctions{
private val PREFS_NAME = "SharedPreferences"
private var preferences: SharedPreferences
init {
preferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
}
override fun setDeviceMAC(deviceMAC: String) {
preferences[DEVICE_MAC] = deviceMAC
}
override fun getDeviceMAC(): String {
return preferences[DEVICE_MAC] ?: ""
}
override fun setLastConnectionTime(lastConnectionTime: String) {
preferences[LAST_CONNECTION_TIME] = lastConnectionTime
}
override fun getLastConnectionTime(): String {
return preferences[LAST_CONNECTION_TIME] ?: ""
}
override fun clearPrefs() {
preferences.edit().clear().apply()
}
companion object{
const val DEVICE_MAC = "yyyyyyy"
const val LAST_CONNECTION_TIME = "zzzzzzz"
}
}
Your arguement context is not a acitivity or fragment, and you need those two to call getSharedPreferences method.
class PreferenceManager(context: Context) : PreferencesFunctions{
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.
I would like to find a way to generate getters and setters of some Kotlin property automatically. In java there is no problem to do it.
I am working with data binding and I have many classes which looks like so:
class AnimalListItemPresenter(private var _animal: String) : BaseObservable() {
var animal: String
#Bindable get() = _animal
set(value) {
_animal = value
notifyPropertyChanged(BR.item)
}
}
I know that it is not possible not possible to generate the logic in setter but can I at leat somehow generate the standard getter and setter?
Standard getters and setters are built into Kotlin.
example:
class Customer {
var id = "",
var name = ""
}
and you can use it like:
fun copyCustomer(customer: Customer) : Customer {
val result = Customer()
result.name = customer.name
.
.
return result
}
You can also override the default getter and setter in the manner you have done in the code snippet. Good Resource: https://kotlinlang.org/docs/reference/properties.html
If you want a quick way of generating boilerplate code in Android Studio -> Alt + Enteron the property and you canAdd GetterorAdd Setter` among different options
class BannerBean {
private var id: String? = null
private var image: String? = null
private var link: String? = null
fun getId(): String? {
return id
}
fun setId(id: String?) {
this.id = id
}
fun getImage(): String? {
return image
}
fun setImage(image: String?) {
this.image = image
}
fun getLink(): String? {
return link
}
fun setLink(link: String?) {
this.link = link
}
}
I want to move from one Activity which displays a RecyclerView to another Activity (detail). But when I added data transmission via Intent, the data always failed to be taken in the Activity detail.
This is the error:
My MainDetail:
private lateinit var viewModel: MainDetailModel
var idAnime: String = "34134"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_detail)
idAnime = intent.getStringExtra("idAnime")
println("idanime $idAnime")
setupFragment()
setupViewModel()
}
}
ViewModel:
class MainViewModel(context: Application, private val appRepository: AppRepository, private val contexts: Context) : AndroidViewModel(context), MainItemClickAction {
override fun onItemClicked(detailModel: DetailModel) {
var intent = Intent(contexts, MainDetailActivity::class.java)
intent.putExtra("idAnime",detailModel.mal_id )
contexts.startActivity(intent)
}
}
Check if your field "detailModel.mal_id" mal_id in that case is string, because you're requesting string in details activity. If it's string check also if this "mal_id" is null. Other issues from code you provided can't be seen.
Check your value idAnime, it exists or not, I think is better check all value is empty or not before putting into listView or another view.
Activity receiving intent
class AddNoteActivity : AppCompatActivity() {
private lateinit var addViewModel: NoteViewModel
private lateinit var titleEditText: TextInputEditText
private lateinit var contentEditText: TextInputEditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_note_activty)
setSupportActionBar(toolbar)
addViewModel = ViewModelProviders.of(this).get(NoteViewModel::class.java)
titleEditText = findViewById(R.id.itemTitle)
contentEditText = findViewById(R.id.itemNote)
val extra = intent.extras
if (extra != null) {
val uuid = extra.getLong("UUID")
val note: Notes? = addViewModel.getNote(uuid)
titleEditText.setText(note!!.title)
contentEditText.setText(note.note)
}
}
}
NoteViewModel class
class NoteViewModel(application: Application) : AndroidViewModel(application) {
companion object {
private var note: Notes = Notes(0, "", "test title", "test ontent")
}
fun getNote(uuid: Long?): Notes {
val job = async(CommonPool) {
getNoteAsyncTask(notesDatabase).execute(uuid)
}
runBlocking { job.await() }
return note
}
class getNoteAsyncTask(database: NotesDatabase) : AsyncTask<Long, Unit, Unit>() {
private val db: NotesDatabase = database
override fun doInBackground(vararg params: Long?) {
note = db.notesDataDao().getNote(params[0])
}
}
}
If I pass an intent to get a Note object from the database with a uuid and set that received data in titleEditText and contentEditText, the data set in the Note was from previous intent invoked when we clicked on the Note item in RecyclerView. On clicking the Note item for the first time, I get the default value which I have set "test title" and "test content".
Aforementioned is the behavior most of the time. Sometimes the data set in titleEditText and contentEditText is of the correct Note object.
Can someone please tell me what I have done wrong? How can I correct my apps behavior?
Unfortunately, there is a big mistake in how you use a view model to provide a data to your view(AddNoteActivity).
Basically, your view never has a chance to wait for the data to be fetched as it always receives a default value. This happens because the AsyncTask runs on its own thread pool so the coroutine completes immediately and returns a default value.
You should consider using LiveData to post a new object to your view and refactor your view model.
So, you need to make a query to the database synchronous and observe changes to a note rather than have a getter for it. Of course, in a real life scenario it might be a good idea to have different kind of states to be able to show a spinner while a user is waiting. But this is another big question. So to keep things simple consider changing your view model to something like that:
class NoteViewModel(private val database: NotesDatabase) : ViewModel { // you do not need an application class here
private val _notes = MutableLiveData<Notes>()
val notes: LiveData<Notes> = _notes
fun loadNotes(uuid: Long) {
launch(CommonPool) {
val notes = database.notesDataDao().getNote(uuid)
_notes.setValue(notes)
}
}
}
Then, you can observe changes to the note field in your activity.
class AddNoteActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
val noteViewModel = ViewModelProviders.of(this).get(NoteViewModel::class.java)
noteViewModel.notes.observe(this, Observer {
title.text = it.title
content.text = it.note
})
}
}
Also you need to use a ViewModelProvider.Factory to create your view model and properly inject dependencies into it. Try to avoid having a context there as it makes it much harder to test.