I have already a Parcelable data class which is used across the module:
#Keep
data class TotalProductValue(val header: String?, val prices: Pricing?, val cta: Cta?) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readParcelable(Pricing::class.java.classLoader),
parcel.readParcelable(Cta::class.java.classLoader),
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(header)
parcel.writeString(subHeader)
parcel.writeParcelable(prices, flags)
parcel.writeParcelable(cta, flags)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<TotalProductValue> {
override fun createFromParcel(parcel: Parcel): TotalProductValue {
return TotalProductValue(parcel)
}
override fun newArray(size: Int): Array<TotalProductValue?> {
return arrayOfNulls(size)
}
}
}
Now I want to add one more parameter as below without impacting other places where it's used, I tried default value and parcel.dataAvail() below but it didn't work. Is there any work around without creating another data class ?
#Keep
data class TotalProductValue(val header: String?, val subHeader: String? = null, val prices: Pricing?, val cta: Cta?) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
if (parcel.dataAvail() > 0) parcel.readString() else null,
parcel.readParcelable(Pricing::class.java.classLoader),
parcel.readParcelable(Cta::class.java.classLoader),
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(header)
parcel.writeString(subHeader)
parcel.writeParcelable(prices, flags)
parcel.writeParcelable(cta, flags)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<TotalProductValue> {
override fun createFromParcel(parcel: Parcel): TotalProductValue {
return TotalProductValue(parcel)
}
override fun newArray(size: Int): Array<TotalProductValue?> {
return arrayOfNulls(size)
}
}
}
Related
I'm working on a weather app for a class and I'm having some trouble with parcelable. I've looked at several other questions that are similar to this on StackOverflow, but haven't found an answer, so I wanted to throw my specific situation out there.
I have this Data class:
package com.example.weatherapp
import android.os.Parcel
import android.os.Parcelable
data class CurrentConditions(
val weather: List<WeatherCondition>,
val main: Currents?,
val name: String?
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readParcelableList(List<WeatherCondition>(), WeatherCondition::class.java.classLoader), <-- This line is giving me trouble
parcel.readParcelable(Currents::class.java.classLoader),
parcel.readString()
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeParcelable(main, flags)
parcel.writeString(name)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<CurrentConditions> {
override fun createFromParcel(parcel: Parcel): CurrentConditions {
return CurrentConditions(parcel)
}
override fun newArray(size: Int): Array<CurrentConditions?> {
return arrayOfNulls(size)
}
}
}
And on the line I've indicated, I'm trying to parcel a list of WeatherCondition objects, which is another parcelable data class:
package com.example.weatherapp
import android.os.Parcel
import android.os.Parcelable
data class WeatherCondition(
val main: String?,
val icon: String?
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString()
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(main)
parcel.writeString(icon)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<WeatherCondition> {
override fun createFromParcel(parcel: Parcel): WeatherCondition {
return WeatherCondition(parcel)
}
override fun newArray(size: Int): Array<WeatherCondition?> {
return arrayOfNulls(size)
}
}
}
The empty parentheses at the end of List<WeatherCondition>() have a red line on them. I'm not sure if I need something in the parentheses or if they shouldn't be there at all. If I remove the () then List<WeatherCondition> has the red line instead. Am I even close to on the right track? (This is my first time writing a mobile app, so I'm on a steep learning curve here).
Thanks for any help anyone can offer.
I want to basically read the List of List from the Parcel in Kotlin constructor, I have my class structured like this:
data class Sports(
var media: List<List<MediaObject>>?,
var likes: Int) : Parcelable {
constructor(parcel: Parcel) : this(
TODO("media"),
parcel.readInt(),
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
if (media == null || media!!.isEmpty()) {
parcel.writeInt(0)
} else {
parcel.writeInt(media!!.size)
for (mediaObjects in media!!) {
parcel.writeTypedList(mediaObjects)
}
}
parcel.writeInt(likes)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Sports> {
override fun createFromParcel(parcel: Parcel): Sports {
return Sports(parcel)
}
override fun newArray(size: Int): Array<Sports?> {
return arrayOfNulls(size)
}
}}
I have a List<List<MediaObject>> which i want to read from the parcel, how can we achieve this, i guess we can have some inline functions but not sure how we'll do this ?
Kotlin automatically adds TODO if its List<List<>> type object.
In Kotlin 1.1.4 was added Parcelable support with #Parcelize annotation
It's more convenient way to deal with it
Check it out:
https://kotlinlang.org/docs/tutorials/android-plugin.html#parcelable
I have a parent class in Kotlin like this
open class Parent(
var name: String,
var dose: JsonElement?,
val other: String?) {
constructor(name: String, dose: JsonElement?)
: this(
name = name.toLowerCase(),
dose = dose,
other = null
)
}
And its child class like this
class Child(
val type: String,
name: String,
dose: JsonElement?
) : Parent(name, dose), Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString() ?: "",
parcel.readString() ?: "",
Gson().fromJson(parcel.readString(), JsonElement::class.java))
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(type)
parcel.writeString(name)
parcel.writeString(Gson().toJson(dose))
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Child> {
override fun createFromParcel(parcel: Parcel): Child {
return Child(parcel)
}
override fun newArray(size: Int): Array<Child?> {
return arrayOfNulls(size)
}
}
}
My problem here is when I pass object of child class as parcelable only child class properties are propagated. All base class properties like name and dose are null after object is de-serialized.
I have cross-verified order of reading and writing and also verified that values are being written correctly at the time of serialization. Let me know if more information is needed from my side.
I have a class that has a member variable of type ArrayList<>. Both classes implement parcelable. I'm stuck on how to complete the class that has a reference to the other class.
Here is what I have:
data class Tab (val name: String, val title: String, val color: String, val sections: ArrayList<Section>) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readTypedList<Section>(sections, Section.CREATOR))
override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeString(name)
dest?.writeString(title)
dest?.writeString(color)
dest?.writeTypedList<Section>(sections)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Tab> {
override fun createFromParcel(parcel: Parcel): Tab {
return Tab(parcel)
}
override fun newArray(size: Int): Array<Tab?> {
return arrayOfNulls(size)
}
}
}
Notice how this class has a value called sections that is an ArrayList<Section>. I need to write that variable to a parcelable, but it's not working.
For reference, here is the Section class. I think this one is OK:
data class Section(val type: String, val text: String, val imageName: String) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
parcel.readString())
override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeString(type)
dest?.writeString(text)
dest?.writeString(imageName)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Section> {
override fun createFromParcel(parcel: Parcel): Section {
return Section(parcel)
}
override fun newArray(size: Int): Array<Section?> {
return arrayOfNulls(size)
}
}
}
It's the readTypedList and writeTypedList lines that are failing.
Thanks for any help.
First solution
#Suppress("UNCHECKED_CAST")
constructor(parcel: Parcel): this(parcel.readString(), parcel.readString(), parcel.readString(), parcel.readArrayList(Tab::class.java.classLoader) as ArrayList<Section>)
Second solution
constructor(parcel: Parcel): this(parcel.readString(), parcel.readString(), parcel.readString(), ArrayList<Section>()){
parcel.readTypedList(sections, Section.CREATOR)
}
I am trying to write a parcelable data object to pass to from activityA to activityB in my android application.
My object is passing with all the data, except my arraylist of the class Available Service
data class AvailableService(val id: Int,
val name: String,
val description: String,
val price: Double,
val currency: String,
val imageUrl: String) : Parcelable {
companion object {
#JvmField #Suppress("unused")
val CREATOR = createParcel { AvailableService(it) }
}
protected constructor(parcelIn: Parcel) : this(parcelIn.readInt(),
parcelIn.readString(),
parcelIn.readString(),
parcelIn.readDouble(),
parcelIn.readString(),
parcelIn.readString())
override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeInt(id)
dest?.writeString(name)
dest?.writeString(description)
dest?.writeDouble(price)
dest?.writeString(currency)
dest?.writeString(imageUrl)
}
override fun describeContents() = 0
}
above is the available serviceClass, next I have Trip, which holds an arraylist of AvailableService.. I observed this in debug, it is successfully writing the arraylist, for some reason I have an issue with reading the data.
data class Trip(val id: String,
val status: String,
val orderedServices: ArrayList<OrderedService>) : Parcelable {
companion object {
#JvmField #Suppress("unused")
val CREATOR = createParcel { Trip(it) }
}
protected constructor(parcelIn: Parcel) : this(parcelIn.readString(),
parcelIn.readString(),
arrayListOf<OrderedService>().apply {
parcelIn.readArrayList(OrderedService::class.java.classLoader)
}
)
override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeString(id)
dest?.writeString(status)
dest?.writeList(orderedServices)
}
override fun describeContents() = 0
}
in case someone wonders what's the fun of the CREATOR does, code below:
inline fun <reified T : Parcelable> createParcel(
crossinline createFromParcel: (Parcel) -> T?): Parcelable.Creator<T> =
object : Parcelable.Creator<T> {
override fun createFromParcel(source: Parcel): T? = createFromParcel(source)
override fun newArray(size: Int): Array<out T?> = arrayOfNulls(size)
}
again, writing succeeds, but reading fails, I get an empty arraylist..
I think that part is the faulty one:
arrayListOf<OrderedService>().apply {
parcelIn.readArrayList(OrderedService::class.java.classLoader)
}
is there a different way to read/write arraylist? am I writing it wrong? reading it wrong?
thanks in advance for any help!
Replace:
arrayListOf<OrderedService>().apply {
parcelIn.readArrayList(OrderedService::class.java.classLoader)
}
with:
arrayListOf<OrderedService>().apply {
parcelIn.readList(this, OrderedService::class.java.classLoader)
}
Change
arrayListOf<OrderedService>().apply {
parcelIn.readArrayList(OrderedService::class.java.classLoader)
}
to
source.createTypedArrayList(OrderedService.CREATOR)
Change dest?.writeList(orderedServices)
to dest?.writeTypedList(orderedServices)
The creator method
companion object {
#JvmField
val CREATOR: Parcelable.Creator<Trip> = object : Parcelable.Creator<Trip> {
override fun createFromParcel(source: Parcel): Trip = Trip(source)
override fun newArray(size: Int): Array<Trip?> = arrayOfNulls(size)
}
}
For API 29 and above
Replace:
arrayListOf<OrderedService>().apply {
parcelIn.readArrayList(OrderedService::class.java.classLoader)
}
with
if (Build.VERSION.SDK_INT >= 29)
parcel.readParcelableList(this, OrderedService::class.java.classLoader)
else
parcel.readList(this as List<OrderedService>, OrderedService::class.java.classLoader)
}
and
override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeString(id)
dest?.writeString(status)
dest?.writeList(orderedServices)
}
with
override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeString(id)
dest?.writeString(status)
if (Build.VERSION.SDK_INT >= 29) {
dest?.writeParcelableList(orderedServices,flags)
} else {
dest?.writeList(orderedServices as List<OrderedService>)
}
}
If Parent and Child model have parcelable then used below one
Parent Model
Parcelable {
constructor(parcel: Parcel) : this(
parcel.createTypedArrayList(ImagesModel.CREATOR)
)
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeTypedList(images)
}