用 Kotlin 在 Android 中创建 Parcelable 数据类有什么方便的方法吗?

我目前正在 Java 项目中使用优秀的 自动包裹,它促进了 Parcelable 类的创建。

现在,Kotlin,我正在考虑我的下一个项目,有一个数据类的概念,自动生成 equals,hashCode 和 toString 方法。

是否有一种方便的方法使 Kotlin 数据类 Parcelable 以一种方便的方式(不需要手动实现这些方法) ?

94001 次浏览

不幸的是,在 Kotlin 没有办法在接口中放入一个真正的字段,所以你不能免费从接口适配器继承它: data class Par : MyParcelable

你可以看代表团,但它不帮助领域,AFAIK: https://kotlinlang.org/docs/reference/delegation.html

所以我看到的唯一选择是 Parcelable.Creator的面料函数,这是显而易见的。

有一个插件,但并不总是像 Kotlin 正在发展的那样更新: Https://plugins.jetbrains.com/plugin/8086

选择: 我有一个使用 Parcelable和列表的自定义数据类的工作示例:

使用带列表的 Parcelable 的数据类:

Https://gist.github.com/juanchosaravia/0b61204551d4ec815bbf

希望能有帮助!

你可以试试这个插件:

Android-parcelable-intellij-plugin-kotlin

它可以帮助你为 kotlin 的数据类生成 Android Parcelable 样板代码,最后看起来像这样:

data class Model(var test1: Int, var test2: Int): Parcelable {


constructor(source: Parcel): this(source.readInt(), source.readInt())


override fun describeContents(): Int {
return 0
}


override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeInt(this.test1)
dest?.writeInt(this.test2)
}


companion object {
@JvmField final val CREATOR: Parcelable.Creator<Model> = object : Parcelable.Creator<Model> {
override fun createFromParcel(source: Parcel): Model{
return Model(source)
}


override fun newArray(size: Int): Array<Model?> {
return arrayOfNulls(size)
}
}
}
}

你试过 包裹吗? 它是一个注释处理器,可以自动为你生成 Android Parcelable样板代码。

用法:

@PaperParcel注释数据类,实现 PaperParcelable,并添加生成的 CREATOR的 JVM 静态实例,例如:

@PaperParcel
data class Example(
val test: Int,
...
) : PaperParcelable {
companion object {
@JvmField val CREATOR = PaperParcelExample.CREATOR
}
}

现在您的数据类是 Parcelable,可以直接传递给 BundleIntent

编辑: 使用最新 API 更新

我将离开我的做事方式,以防它可能帮助某人。

我做的是我有一个通用 Parcelable

interface DefaultParcelable : Parcelable {
override fun describeContents(): Int = 0


companion object {
fun <T> generateCreator(create: (source: Parcel) -> T): Parcelable.Creator<T> = object: Parcelable.Creator<T> {
override fun createFromParcel(source: Parcel): T = create(source)


override fun newArray(size: Int): Array<out T>? = newArray(size)
}


}
}


inline fun <reified T> Parcel.read(): T = readValue(T::class.javaClass.classLoader) as T
fun Parcel.write(vararg values: Any?) = values.forEach { writeValue(it) }

然后我创建像这样的包裹:

data class MyParcelable(val data1: Data1, val data2: Data2) : DefaultParcelable {
override fun writeToParcel(dest: Parcel, flags: Int) { dest.write(data1, data2) }
companion object { @JvmField final val CREATOR = DefaultParcelable.generateCreator { MyParcelable(it.read(), it.read()) } }
}

这样我就能摆脱那个模板覆盖了。

我更喜欢使用 https://github.com/johncarl81/parceler

@Parcel(Parcel.Serialization.BEAN)
data class MyClass(val value)

最好的的方式与 没有样板代码在所有是 走私贩级插件。您所需要的只是实现 AutoParcelable 接口,如

data class Person(val name:String, val age:Int): AutoParcelable

这就是全部。工程密封类也。此外,这个插件提供编译时验证所有 AutoParcelable 类。

UPD 17.08.2017 现在使用 Kotlin 1.1.4和 Kotlin Android 扩展插件你可以使用 @Parcelize注释:

@Parcelize class Person(val name:String, val age:Int): Parcelable

不需要 data修饰符。目前,最大的缺点是使用 kotlin-android 扩展插件,该插件还有很多其他可能不必要的功能。

通过使用 安卓工作室科特林插件,我找到了一种简单的方法来用 没有额外的插件转换我的旧 Java Parcelable(如果你想要的只是将一个全新的 data类转换成 Parcelable,那么跳到第4个代码片段)。

假设你有一个 Person类,所有的 Parcelable锅炉板:

public class Person implements Parcelable{
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}


@Override
public Person[] newArray(int size) {
return new Person[size];
}
};


private final String firstName;
private final String lastName;
private final int age;


public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}


protected Person(Parcel in) {
firstName = in.readString();
lastName = in.readString();
age = in.readInt();
}


@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(firstName);
dest.writeString(lastName);
dest.writeInt(age);
}


@Override
public int describeContents() {
return 0;
}


public String getFirstName() {
return firstName;
}


public String getLastName() {
return lastName;
}


public int getAge() {
return age;
}
}

从剥离 Parcelable实现开始,留下一个基本的、普通的、旧的 爪哇咖啡对象(属性应该是 final 并由构造函数设置) :

public class Person {
private final String firstName;
private final String lastName;
private final int age;


public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}


public String getFirstName() {
return firstName;
}


public String getLastName() {
return lastName;
}


public int getAge() {
return age;
}
}

然后,让 Code > Convert Java file to Kotlin File期权发挥它的魔力:

class Person(val firstName: String, val lastName: String, val age: Int)

将其转换为 data类:

data class Person(val firstName: String, val lastName: String, val age: Int)

最后,让我们再次把它变成 Parcelable。悬停类名,Android Studio 应该会给你 Add Parcelable Implementation的选项。结果应该是这样的:

data class Person(val firstName: String, val lastName: String, val age: Int) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
parcel.readInt()
)


override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(firstName)
parcel.writeString(lastName)
parcel.writeInt(age)
}


override fun describeContents(): Int {
return 0
}


companion object CREATOR : Parcelable.Creator<Person> {
override fun createFromParcel(parcel: Parcel): Person {
return Person(parcel)
}


override fun newArray(size: Int): Array<Person?> {
return arrayOfNulls(size)
}
}
}

如您所见,Parcelable实现是附加到 data类定义的一些自动生成的代码。

备注:

  1. 试图将 爪哇咖啡 Parcelable直接转换为 科特林将不会产生与当前版本的 科特林插件(1.1.3)相同的结果。
  2. 我不得不删除一些额外的花括号当前 Parcelable代码生成器引入。必须是一个小错误。

我希望这条建议对你和对我一样有效。

Kotlin 1.1.4 出局

Android 扩展插件现在包含一个自动 Parcelable 实现生成器。在主构造函数中声明序列化的属性,并添加@Parcelize 注释,就会自动创建 writeToParcel ()/createFromParcel ()方法:

@Parcelize
class User(val firstName: String, val lastName: String) : Parcelable

所以你需要让他们把这个添加到你模块的 建造,分级:

apply plugin: 'org.jetbrains.kotlin.android.extensions'


android {
androidExtensions {
experimental = true
}
}

只需点击 kotlin 数据类的 data 关键字,然后按 alt + Enter,选择第一个选项,选择 "Add Parceable Implementation"

Kotlin 已经用它的@Parcel 注释使得 Android 中的 Parcelization 的整个过程变得非常简单。

做到这一点

步骤1. 在应用模块级别中添加 Kotlin 扩展

步骤2. 添加实验 = 真,因为这个特性仍然在 gradle 中处于实验阶段。

安卓扩展{ 实验性的 = 真实的 }

步骤3. 使用@Parcel 来 Annonate 数据类

这里 是@Parcel 用法的一个简单示例

  • 在模型/数据类之上使用 @ Parcelize注释
  • 使用最新版本的 Kotlin
  • 在应用程序模块中使用最新版本的 Kotlin Android 扩展

例如:

@Parcelize
data class Item(
var imageUrl: String,
var title: String,
var description: Category
) : Parcelable

您可以使用 @Parcelize注释来完成。

首先,您需要允许他们将这些内容添加到模块的 build.gradle 中:

apply plugin: 'org.jetbrains.kotlin.android.extensions'

在主构造函数中声明序列化属性并添加 @Parcelize注释,writeToParcel()/createFromParcel()方法将自动创建:

@Parcelize
class User(val firstName: String, val lastName: String) : Parcelable

您需要在 androidExtensions块中添加 experimental = true

下面的 Kotlin 代码可以帮助您使用 Parent 和 Child Data 类创建 Parcelable 数据类

父数据类 -MyPostModel

data class MyPostModel(
@SerializedName("post_id") val post_id: String? = "",
@SerializedName("category_id") val category_id: String? = "",
@SerializedName("images") val images: ArrayList<ImagesModel>? = null
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
parcel.createTypedArrayList(ImagesModel.CREATOR)
)


override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(post_id)
parcel.writeString(category_id)
parcel.writeTypedList(images)
}


override fun describeContents(): Int {
return 0
}


companion object CREATOR : Parcelable.Creator<MyPostModel> {
override fun createFromParcel(parcel: Parcel): MyPostModel {
return MyPostModel(parcel)
}


override fun newArray(size: Int): Array<MyPostModel?> {
return arrayOfNulls(size)
}
}
}

子数据类-图像模型

data class ImagesModel(
@SerializedName("image_id") val image_id: String? = "",
@SerializedName("image_url") val image_url: String? = ""
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString()
)


override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(image_id)
parcel.writeString(image_url)
}


override fun describeContents(): Int {
return 0
}


companion object CREATOR : Parcelable.Creator<ImagesModel> {
override fun createFromParcel(parcel: Parcel): ImagesModel {
return ImagesModel(parcel)
}


override fun newArray(size: Int): Array<ImagesModel?> {
return arrayOfNulls(size)
}
}
}

一个更简单和最新的方法是使用 @Parcelize注释,它消除了实现 Parcelable 的繁重工作

Gradle (app 模块)

apply plugin: "kotlin-parcelize"


// If you are using the new plugin format use this instead.
plugins{
...
id "kotlin-parcelize"
}

YourClass.kt

import kotlinx.parcelize


@Parcelize
class User(val firstName: String, val lastName: String, val age: Int): Parcelable

由于没有人提到这一点(或者更确切地说,没有提供版本的具体细节) ,我将很快就此发表评论:

我使用上述指南尝试了 @Parcelize的所有可能的组合,但是没有成功地使其工作。

如果您的 Android 工作室显示错误,您的类 describeContentswriteToParcel没有实现,即使与 @Parcelize注释,请检查在您的 app/build.gradle您没有启用 apply plugin: 'kotlin-android-extensions'

由于某种原因,我的项目使用了旧版本的不推荐的扩展(KtxVersion =’1.0.2’)。 所以这与 @Parcelize的实现有冲突。

当我试图添加 apply plugin: 'kotlin-parcelize'时,它说它不能同时使用扩展,我只能使用其中一个。

在和同事一起工作之后,我们发现扩展导致了整个 @Parcelize的失败。我尝试了很多不同的方法,但是即使在克服了编译时错误之后,运行时错误也会说“ CREATER”没有发现异常或类似的情况。

所以最后我删除了 kotlin-android-extensions应用插件,只包含了 apply plugin: 'kotlin-parcelize',它修复了这个问题,并且按照预期工作。