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
}
}
Related
I have a problem with an object namely when creating a list of objects it gets an error like in the title.
Kotlin code
private fun readNotification() {
val firebaseUser = FirebaseAuth.getInstance().currentUser
val reference = FirebaseDatabase.getInstance().getReference("Notifications")
.child(firebaseUser!!.uid)
reference.addValueEventListener(object : ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
notificationList.clear()
for (snapshot : DataSnapshot in dataSnapshot.children){
val notification = snapshot.getValue(NotificationData::class.java)!!
notificationList.add(notification)
}
notificationList.reverse()
notificationAdapter.notifyDataSetChanged()
}
override fun onCancelled(error: DatabaseError) {
}
})
}
Object code
class NotificationData(
var userId: String = "",
var text: String = "",
var postId: String = "",
var isPost: Boolean = false
) {}
Firebase structure
I know the error is on this line because the object has a boolean variable
val notification = snapshot.getValue(NotificationData::class.java)!!
I just don't know much how to deal with it now
While I thought the Firebase JSON mapper handled isBla format getters, there is only one boolean in your JSON, so it makes sense that the problem comes form there.
You could change the boolean to this:
var post: Boolean = false
Or add an explicit #PropertyName annotation:
#PropertyName("post")
var isPost: Boolean = false
That said, I think it's a valid expectation for the Firebase mapper to handle boolean isXyz properties by default, so would recommend adding a feature request on the Github repo of the SDK.
Im trying to apply my converters to room model and after many tries it still gives me
private java.util.List<pl.beskidmedia.bm.tv.retrofit.EpgShort> epg;
^C:\...\bm\build\tmp\kapt3\stubs\debug\pl\beskidmedia\bm\tv\cache\Channels.java:21: error: Cannot figure out how to read this field from a cursor.
private java.util.List<pl.beskidmedia.bm.tv.retrofit.EpgShort> epg;
^
Im trying to convert list to string to save it to a database and to achive it im using this
class TypeConverters {
companion object {
#TypeConverter
#JvmStatic
fun toList(string: String?): List<EpgShort>? {
val listType = object : TypeToken<List<EpgShort>>() {}.type
return if (string != null) {
Gson().fromJson<List<EpgShort>>(string, listType)
} else
null
}
#TypeConverter
#JvmStatic
fun fromList(list: List<EpgShort>?): String? {
val type = object : TypeToken<List<EpgShort>>() {}.type
return if (list != null) {
Gson().toJson(list, type)
} else {
null
}
}
}
}
and this is my entity
#Entity
data class Channels(
#PrimaryKey(autoGenerate = false) val id: Int,
val name: String,
val number: Int,
val cmd: String,
val logo: String,
val allowPvr: Int,
val lastUpdate: Long,
#field:TypeConverters(pl.beskidmedia.bm.tv.cache.TypeConverters::class)
var epg: List<EpgShort>
)
The database itself was working before i added this secound table so I thing it should be just it.
Ok, I found the bug, everything was fine but I imported similar named class from different package by mistake, ty for all clues and comments and sorry for this mistake.
I am new to Kotlin and have been developing with the language. From Java, I am used to coding getters and setters by creating two functions. For example:
public String getName(){
return name;
}
public void setName(name){
this.name = name;
}
However, can this code be simplified in Kotlin? My code right now is:
class ClassName{
private var username: String? = null
private var photoFileName: String? = null
private var userId: String? = null
private var requestSent: Boolean? = null
fun ClassName(username: String?, photoFileName: String?, userId: String?, requestSent: Boolean?) {
this.username = username
this.photoFileName = photoFileName
this.userId = userId
this.requestSent = requestSent
}
fun getUsername(): String? {
return username
}
fun setUsername(string: String){
username = string
}
fun getPhotoFileName(): String? {
return photoFileName
}
fun setPhotoFileName(string: String){
photoFileName = string
}
fun getUserId(): String? {
return userId
}
fun setUserId(string: String){
userId = string
}
fun getRequestSent(): Boolean? {
return requestSent
}
fun setRequestSent(bool: Boolean){
requestSent = bool
}
}
Here's a more enhanced version of your kotlin class
data class YourClass(
var username: String? = null,
var photoFilename: String? = null,
var userId: String? = null,
var requestSent: Boolean? = null
)
You don't have to manually create setter, getter function in Kotlin.
Your class will get converted to this if you use data class in kotlin. All the setters and getters will be replaced by the properties.And yes you can always call them like you used to do like set and get.
data class ClassName(
var username: String,
var photoFileName: String,
var userId: String,
var requestSent: String
)
I have a model
data class RegisterPostDataWithPwdCheck(
var phone_number: String?,
var name: String?,
var password: String?,
var secondPassword: String?)
And a ViewModel
class SignUpViewModel(application: Application) : BaseViewModel(application){
val registerPostData = MutableLiveData<RegisterPostDataWithPwdCheck>...
fun checkPassword(){}...}
I also have a View that has this code inside
viewModel.registerPostData.observe(viewLifecycleOwner, Observer {
viewModel.checkPassword()
})
In the XML there are two fields of interest
<EditText
android:id="#+id/edittext_sign_up_password"
android:text="#={view_model.registerPostData.password}" />
<EditText
android:id="#+id/edittext_sign_up_second_pw"
android:text="#={view_model.registerPostData.secondPassword}" />
What I understood so far is that the .observe will be called only when the entire RegisterPostDataWithPwdCheck object changes and I don't want that. I want it to be triggered when any of the parameters changes so I can call the fun checkPassword(){} in order to see if the two fields match. Is this possible?
Using #mahdi-shahbazi comment I've managed to work this out in Kotlin. My Model is now:
data class RegisterPostDataWithPwdCheck(
#SerializedName(value = "phone_number")
private var phoneNumber: String?,
private var name: String?,
private var password: String?,
private var secondPassword: String?
) : BaseObservable() {
#Bindable
fun getPhoneNumber(): String? {
return phoneNumber
}
fun setPhoneNumber(value: String) {
if (value != phoneNumber) {
phoneNumber = value
notifyPropertyChanged(BR.phoneNumber)
}
}
#Bindable
fun getName(): String? {
return name
}
fun setName(value: String?) {
if (value != name) {
name = value
notifyPropertyChanged(BR.name)
}
}
#Bindable
fun getPassword(): String? {
return password
}
fun setPassword(value: String?) {
if (value != password) {
password = value
notifyPropertyChanged(BR.password)
}
}
#Bindable
fun getSecondPassword(): String? {
return secondPassword
}
fun setSecondPassword(value: String?) {
if (value != secondPassword) {
secondPassword = value
notifyPropertyChanged(BR.secondPassword)
}
}
}
And creating custom LiveData class:
class PropertyAwareMutableLiveData<T : BaseObservable> : MutableLiveData<T>()
{
private val callback = object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
value = value
}
}
override fun setValue(value: T?) {
super.setValue(value)
value?.addOnPropertyChangedCallback(callback)
}
}
What I still don't know if there is a way to automate this #Binding process which is terribly slow and boring and also forces some changes (turning parameters to private).
Extension functions are great for the SharedPreference api in android. Jake Wharton has an interesting implementation at time code 32:30 of this video tutorial where he implements SharedPreferences extension function like so:
preferences.edit{
set(USER_ID /*some string key constant somewhere*/, 42)
//...
}
while this is ok, its kind of verbose.
This tutorial by Krupal Shah explains how you can reduce the getter/setter extension functions of SharedPreferences to:
preferences[USER_ID] = 42
Log.i("User Id", preferences[USER_ID]) //User Id: 42
This is pretty good, but the brackets imply iterable semantics, IMO. While not the worst thing in the world, you just wish that you could implement a field extension of a SharedPreferences value by the key constant itself.
My question is, is there any way to implement this type of extension on SharedPreferences?
preferences.USER_ID = 42
Log.i("User Id", preferences.USER_ID) //User Id: 42
First, let's create general interface for providing instance of SharedPreferences:
interface SharedPreferencesProvider {
val sharedPreferences: SharedPreferences
}
After we have to create delegate for property which will read/write value to preferences:
object PreferencesDelegates {
fun string(
defaultValue: String = "",
key: String? = null
): ReadWriteProperty<SharedPreferencesProvider, String> =
StringPreferencesProperty(defaultValue, key)
}
private class StringPreferencesProperty(
private val defaultValue: String,
private val key: String?
) : ReadWriteProperty<SharedPreferencesProvider, String> {
override fun getValue(
thisRef: SharedPreferencesProvider,
property: KProperty<*>
): String {
val key = key ?: property.name
return thisRef.sharedPreferences.getString(key, defaultValue)
}
override fun setValue(
thisRef: SharedPreferencesProvider,
property: KProperty<*>,
value: String
) {
val key = key ?: property.name
thisRef.sharedPreferences.save(key, value)
}
}
PreferencesDelegates needed to hide implementation and add some readability to code. In the end it can be used like this:
class AccountRepository(
override val sharedPreferences: SharedPreferences
) : SharedPreferencesProvider {
var currentUserId by PreferencesDelegates.string()
var currentUserName by string() //With import
var currentUserNickname by string(key = "CUSTOM_KEY", defaultValue = "Unknown")
fun saveUser(id: String, name: String) {
this.currentUserId = id
this.currentUserName = name
}
}
Similar can be implemented int, float or even custom type:
open class CustomPreferencesProperty<T>(
defaultValue: T,
private val key: String?,
private val getMapper: (String) -> T,
private val setMapper: (T) -> String = { it.toString() }
) : ReadWriteProperty<SharedPreferencesProvider, T> {
private val defaultValueRaw: String = setMapper(defaultValue)
override fun getValue(
thisRef: SharedPreferencesProvider,
property: KProperty<*>
): T {
val key = property.name
return getMapper(thisRef.sharedPreferences.getString(key, defaultValueRaw))
}
override fun setValue(
thisRef: SharedPreferencesProvider,
property: KProperty<*>,
value: T
) {
val key = property.name
thisRef.sharedPreferences.save(key, setMapper(value))
}
}
I wrote small library which covers such case. You can find rest of implemented preferences here
EDIT. In case if you are using dagger:
class AccountRepository #Injcet constructor() : SharedPreferencesProvider {
#Inject
override lateinit var sharedPreferences: SharedPreferences
var currentUserId by PreferencesDelegates.string()
...
}
You could define a simple extension property with a getter and a setter
var SharedPreferences.userId
get() = getInt(USER_ID, 0)
set(value: Int) { edit().putInt(USER_ID, value).apply() }