What does a CREATOR field look like? - android

My Parcelable class wants me to include a CREATOR field, but I have no idea what such a field looks like. I have silenced the complaint with SupressLint in the meanwhile but would like to have that CREATOR field. Any idea what it looks like?
#SuppressLint("ParcelCreator")
#Parcelize
class RecipeTemplate: Parcelable {
var recipeHeader: String? = null
var recipeText: String? = null
var recipeImage: String? = null
var recipeKey: String? = null
}

As Joao Alves says:
There’s still one small problem with Parcelize though. At the moment
there’s an issue with Android Studio showing an error about an
incomplete implementation of the Parcelable interface:
This is a known bug in the IDE itself and you can ignore it, there’s
nothing wrong with the code and it works as expected. You can keep
track of the issue here. At the moment it’s In Progress state.
So you can just ignore this warning.

A sample parcelable implementation would look like this:
data class WarriorParcelable(val name : String,
val weapon : String) : Parcelable {
companion object CREATOR : Parcelable.Creator<WarriorParcelable> {
override fun createFromParcel(parcel: Parcel): WarriorParcelable = WarriorParcelable(parcel)
override fun newArray(size: Int): Array<WarriorParcelable?> = arrayOfNulls(size)
}
private constructor(parcel: Parcel) : this (parcel.readString(),parcel.readString())
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(name)
dest.writeString(weapon)
}
override fun describeContents(): Int = 0
}
However, if you are using the #Parcelize, you don't need to write the implementation codes for the Parcelable. It will be implemented for you.

#Parcelize is still an experimental feature, upgrade to the latest kotlin plugin version and add this code to your build.grade file
android {
...
androidExtensions {
experimental = true
}
}
The IDE will stop complaining. As you can see below; I modified your class a bit but it's essentially this same thing.
#Parcelize
data class RecipeTemplate(var recipeHeader: String? = null,
var recipeText: String? = null,
var recipeImage: String? = null,
var recipeKey: String? = null) : Parcelable

Related

Android: error: non-static type variable T cannot be referenced from a static context

I have the following class:
#Parcelize
data class Collection<T : Parcelable> constructor(
var models: List<T>,
var cursor: String?
) : Parcelable
When I was using Kotlin 1.4.10, the project builds correctly without any errors, then I updated the project to Kotlin 1.4.21, and migrated to use kotlin-parcelize instead of kotlin-android-extensions, so after Kotlin update, when building the project I get the following errors:
> Task :domain:kaptDebugKotlin FAILED
/Library/DevelopmentArea/workspace/baaz_android_new/clean_domain/domain/build/tmp/kapt3/stubs/debug/com/myapp/domain/model/Collection.java:101: error: non-static type variable T cannot be referenced from a static context
public final com.myapp.domain.model.Collection<T>[] newArray(int size) {
^/Library/DevelopmentArea/workspace/baaz_android_new/clean_domain/domain/build/tmp/kapt3/stubs/debug/com/myapp/domain/model/Collection.java:110: error: non-static type variable T cannot be referenced from a static context
public final com.myapp.domain.model.Collection<T> createFromParcel(#org.jetbrains.annotations.NotNull()
Note: I'm using Android Studio 4.1.1
Currently to fix the errors that I get, and meanwhile, keep using Kotlin 1.4.21, I just removed the #Parcelize annotation from any class with a generic type usage like the one in the question, and just implemented the Parcelable with the old way like the following:
data class Collection<T> constructor(
var models: List<T>,
var cursor: String?
) : Parcelable {
constructor(parcel: Parcel) : this(
mutableListOf<T>().also { list: List<T> ->
parcel.readList(list, Collection<T>::models.javaClass.classLoader)
},
parcel.readString())
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeList(models)
parcel.writeString(cursor)
}
override fun describeContents(): Int = 0
companion object {
#JvmField
val CREATOR = object : Parcelable.Creator<Collection<Parcelable>> {
override fun createFromParcel(source: Parcel): Collection<Parcelable> {
return Collection(source)
}
override fun newArray(size: Int): Array<Collection<Parcelable>?> {
return arrayOfNulls(size)
}
}
}
}

Kotlin, Dagger and Realm - DI concept problem

I'm starting this brand new project only for fun, but at the first steps I got a problem, there it goes:
I have this class "Note", it's a realm class as you can see below
#RealmClass
open class Note
#Inject constructor (#PrimaryKey var id: String,
var text: String,
var badge: NoteBadge?
) : RealmObject() {
fun getRandomNoteText(): String = "supposed to be random"
}
Then I have my Main activity class, where I already got Dagger working.
class MainActivity : AppCompatActivity() {
#Inject lateinit var note: Note
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
DaggerNoteComponent.create().inject(this)
Log.d("MAIN_ACTIVITY", note.getRandomNoteText())
Log.d("MAIN_ACTIVITY", note.badge?.getRandomBadgeText())
}
}
It got tricky in terms of concepts, the code above doesn't compile, I have to add this line to my Note class to make it work:
constructor(): this(UUID.randomUUID().toString(), "", NoteBadge()){}
However there I have NoteBadge() I'm creating an instance of NoteBadge manually, that's bad, I would like dagger did that.
I've tried sending a null value as parameter, but them I have a null NoteBadge at my MainActivity.
So do you have any idea how to fix that and make my dependency injection fully funcional? With no manual instance initializations?
EDIT -> Paste NoteComponent
#Component(modules = [NoteBadgeModule::class])
interface NoteComponent {
fun inject(activity: MainActivity)
}

Create data class that is Parcelable

I am developing Android project in Kotlin. I want to create a model class that implements Parcelable interface. This is what I tried:
#Parcelize
data class School(
#Expose val name: String?,
#Expose val address: String?,
): Parcelable
But I get compiler error saying that "Class School is not abstract and does not implement abstract memeber public abstract fun writeToParcel(p0: Parcel!, p1: Int):Unit defined in android.os.Parcelable".
I understand what the error is saying. But how to get rid of this error? My Kotlin version is 1.3.50
Add
androidExtensions {
experimental = true
}
to your Android block within your app build.gradle.
check you have Added this
androidExtensions {
experimental = true
}
and try to implement Parcelable like this
class School(var name:String, var address:String):Parcelable
pass data like this
val school = School("demo","demo")
val intent = Intent(this, activityB::class.java)
intent.putExtra("schooldata",school)
startActivity(intent)
get like this
var school = intent.extras.getParcelable<School>("schooldata")
schooldata.setText("${school.name}"+"\n"+"${school.address}")

Android data binding with Kotlin, BaseObservable, and a custom delegate

I'm attempting to write a custom delegate which would clean up the syntax for databinding in a Kotlin class. It would eliminate the need to define a custom getter and setter for every property I might want to observe.
The standard implementation in Kotlin appears to be as follows:
class Foo : BaseObservable() {
var bar: String
#Bindable get() = bar
set(value) {
bar = value
notifyPropertyChanged(BR.bar)
}
}
Clearly, with a lot of properties this class can become pretty verbose. What I would like instead is to abstract that away into a delegate like so:
class BaseObservableDelegate(val id: Int, private val observable: BaseObservable) {
#Bindable
operator fun getValue(thisRef: Any, property: KProperty<*>): Any {
return thisRef
}
operator fun setValue(thisRef: Any, property: KProperty<*>, value: Any) {
observable.notifyPropertyChanged(id)
}
}
Then, the class which extends BaseObservable could go back to having one-line variable declarations:
class Foo : BaseObservable() {
var bar by BaseObservableDelegate(BR.bar, this)
}
The problem is that without the #Bindable annotation in the Foo class, no propertyId is generated in BR for bar. I'm unaware of any other annotation or method for generating that property id.
Any guidance would be appreciated.
You can annotate the default getter or setter without providing a body.
var bar: String by Delegates.observable("") { prop, old, new ->
notifyPropertyChanged(BR.bar)
}
#Bindable get
There is a shortcut annotation use-site target which does the same thing.
#get:Bindable var bar: String by Delegates.observable("") { prop, old, new ->
notifyPropertyChanged(BR.bar)
}
Additionaly to the accepted answer - sometimes you need variables passed in constructor. It is easy to do too.
class Foo(_bar: String) : BaseObservable() {
#get:Bindable var bar by Delegates.observable(_bar) { _, _, _ ->
notifyPropertyChanged(BR.bar)
}
}
Sometimes we have to save object using parcel, I had some problems using delegete, so code looks like this:
#Parcelize
class Foo(private var _bar: String) : BaseObservable(), Parcelable {
#IgnoredOnParcel
#get:Bindable var bar
get() = _bar
set(value) {
_bar = value
notifyPropertyChanged(BR.bar)
}
}
I considered using the androidx.databinding.ObservableField wrapper for my fields. However, it was quite annoying having to read the values as field.get() and write them field.set(value) from the Kotlin code. Also, this approach does require special converters for serialization if you are using it with Retrofit or Room Database.
Finally, I came up with the below approach which allows me to define the variable in a single line as oppose to the accepted answer and keep the field to their default type without any wrapper. Thanks to the Kotlins property delegation. Now, I don't have to write converters for the serialization and have all the benefit from databinding.
class ObservableField<T : BaseObservable, V>(initialValue: V, private val fieldId: Int = -1) : ReadWriteProperty<T, V> {
private var value: V = initialValue
override fun getValue(thisRef: T, property: KProperty<*>): V {
return value
}
override fun setValue(thisRef: T, property: KProperty<*>, value: V) {
this.value = value
if (fieldId == -1) {
thisRef.notifyChange()
} else {
thisRef.notifyPropertyChanged(fieldId)
}
}
}
class Credential: BaseObservable() {
var username: String by ObservableField("")
#get:Bindable var password: String by ObservableField("", BR.password)
}

Android Parcelable in Kotlin: CREATOR not found on Parcelable data class

With the release of the Kotlin RC, I started writing an app to learn it however I can not figure out how to get Parcelable to work.
the data class:
data class Project (val reponame:String,
val username:String,
val language:String,
val vcsUrl:String,
val branches:Map<String, Branch>) : Parcelable {
companion object {
val CREATOR = object : Parcelable.Creator<Project> {
override fun createFromParcel(`in`: Parcel): Project {
return Project(`in`)
}
override fun newArray(size: Int): Array<Project?> {
return arrayOfNulls(size)
}
}
}
protected constructor(parcelIn: Parcel) : this (
parcelIn.readString(),
parcelIn.readString(),
parcelIn.readString(),
parcelIn.readString(),
mapOf<String, Branch>().apply {
parcelIn.readMap(this, Branch::class.java.classLoader)
}
)
override fun describeContents(): Int {
throw UnsupportedOperationException()
}
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(reponame)
dest.writeString(username)
dest.writeString(language)
dest.writeString(vcsUrl)
dest.writeMap(branches)
}
}
Reading it:
class ProjectDetailActivity : BaseActivity() {
lateinit var project: Project
companion object {
const val EXTRA_PROJECT = "extra_project"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
project = intent.extras.getParcelable(EXTRA_PROJECT)
tvTitle.text = project.reponame
}
}
The exception:
Caused by: android.os.BadParcelableException: Parcelable protocol requires a Parcelable.Creator object called CREATOR on class com.eggman.circleciandroid.model.Project
at android.os.Parcel.readParcelableCreator(Parcel.java:2415)
at android.os.Parcel.readParcelable(Parcel.java:2337)
at android.os.Parcel.readValue(Parcel.java:2243)
at android.os.Parcel.readArrayMapInternal(Parcel.java:2592)
at android.os.BaseBundle.unparcel(BaseBundle.java:221)
at android.os.BaseBundle.get(BaseBundle.java:281)
at com.eggman.circleciandroid.ui.ProjectDetailActivity.onCreate(ProjectDetailActivity.kt:22)
I am sure it is something simple I am missing, has anyone else had success with Parcelable on latest Kotlin?
Kotlin Version: 1.0.0-rc-1036
Kotlin Plugin Version: 1.0.0-rc-1036-IJ143-4
Code is viewable # https://github.com/eggman87/circle-kotlin
Kotlin RC dropped previously deprecated generation of static fields for all companion object properties (learn more in this answer).
Now only those marked by const, lateinit or #JvmField will have a static field generated.
You need to annotate val CREATOR by #JvmField annotation since Android Framework expects a static field CREATOR in your class.
Here you have some useful Kotlin extension functions that will help you to create your CREATORs and also some examples (using data classes and list inside the data class)
Gist: Data Class & Parcelables example
I'm using this code in an Android App: (link)
The same code you can find it here: (link)

Categories

Resources