有没有一种方便的方法可以使用 Kotlin 在 Android 中创建 Parcelable 数据 类?
Is there a convenient way to create Parcelable data classes in Android with Kotlin?
我目前在我的 Java 项目中使用优秀的 AutoParcel,它有助于创建 Parcelable classes。
现在,我考虑将其用于下一个项目的 Kotlin 具有这种数据概念 classes,它会自动生成 equals、hashCode 和 toString 方法。
有没有方便的方法使 Kotlin 数据 class 以方便的方式可打包(无需手动实现方法)?
不幸的是,Kotlin 无法在接口中放置一个真实的字段,因此您不能免费从接口适配器继承它:
data class Par : MyParcelable
您可能会查看委派,但它对字段没有帮助,AFAIK:https://kotlinlang.org/docs/reference/delegation.html
所以我看到的唯一选择是 Parcelable.Creator
的结构函数,这很明显。
有一个插件,但并不总是随着 Kotlin 的发展而更新:
https://plugins.jetbrains.com/plugin/8086
选择:
我有一个使用 Parcelable
和列表的自定义数据 class 的工作示例:
数据 classes 使用带有列表的 Parcelable:
希望对您有所帮助!
你可以试试这个插件:
android-parcelable-intellij-plugin-kotlin
它帮助您为 kotlin 数据 class 生成 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)
}
}
}
}
你试过PaperParcel了吗?它是一个注释处理器,可以自动为您生成 Android Parcelable
样板代码。
用法:
用 @PaperParcel
注释您的数据 class,实施 PaperParcelable
,并添加生成的 CREATOR
的 JVM 静态实例,例如:
@PaperParcel
data class Example(
val test: Int,
...
) : PaperParcelable {
companion object {
@JvmField val CREATOR = PaperParcelExample.CREATOR
}
}
现在你的数据class是Parcelable
,可以直接传递给Bundle
或Intent
编辑:更新最新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) }
然后我像这样创建 parcelables:
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)
完全没有样板代码的最佳方式是Smugglergradle插件。您所需要的只是实现 AutoParcelable 接口,例如
data class Person(val name:String, val age:Int): AutoParcelable
仅此而已。也适用于密封 类。此插件还为所有 AutoParcelable 类 提供编译时验证。
UPD 17.08.2017 现在 Kotlin 1.1.4 and Kotlin Android extensions plugin 您可以使用 @Parcelize
注释。在这种情况下,上面的示例将如下所示:
@Parcelize class Person(val name:String, val age:Int): Parcelable
不需要 data
修饰符。目前最大的缺点是使用 kotlin-android-extensions 插件,它有很多其他可能不需要的功能。
使用 Android Studio 和 Kotlin 插件,我找到了一种简单的方法来转换我的旧 Java Parcelable
s 没有额外的插件(如果你只想把一个全新的 data
class 变成 Parcelable
,跳到第 4 个代码片段)。
假设您有一个 Person
class 和所有 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
实现,留下一个基本的、普通的、旧的 Java 对象(属性应该是最终的并由构造函数):
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
class:
data class Person(val firstName: String, val lastName: String, val age: Int)
最后,让我们再次将其变成 Parcelable
。将鼠标悬停在 class 名称上,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
class 定义。
备注:
- 尝试将 Java
Parcelable
直接转换为 Kotlin 不会产生与Kotlin 插件的当前版本 (1.1.3
).
- 我不得不删除当前
Parcelable
代码生成器引入的一些额外的花括号。应该是小bug。
我希望这个技巧对你和我一样有用。
Android 扩展插件现在包含一个自动 Parcelable 实现生成器。在主构造函数中声明序列化属性并添加@Parcelize 注释,writeToParcel()/createFromParcel() 方法将自动创建:
@Parcelize
class User(val firstName: String, val lastName: String) : Parcelable
所以你需要启用他们将这个添加到你模块的 build.gradle:
apply plugin: 'org.jetbrains.kotlin.android.extensions'
android {
androidExtensions {
experimental = true
}
}
只需单击您的 kotlin 数据的数据关键字 class,然后按 alt+Enter,select 第一个选项显示 "Add Parceable Implementation"
Kotlin 通过其 @Parcel 注释使 Android 中的整个 Parcelization 过程非常简单。
这样做
步骤 1. 在您的应用模块中添加 Kotlin 扩展 gradle
第 2 步。 添加实验性 = true,因为此功能在 gradle 中仍在实验中。
androidExtensions {
experimental = true
}
步骤 3. 使用 @Parcel
注释数据 class
Here 是@Parcel 用法的简单示例
- 在模型/数据之上使用 @Parcelize 注释 class
- 使用最新版本的 Kotlin
- 在您的应用模块中使用最新版本的 Kotlin Android 扩展
示例:
@Parcelize
data class Item(
var imageUrl: String,
var title: String,
var description: Category
) : Parcelable
您可以使用 @Parcelize
注释来完成。它是一个自动 Parcelable 实现生成器。
首先,您需要启用它们,将其添加到您模块的 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 代码可以帮助您创建 Parcelable 数据 classes with Parent and Child Data class
父数据Class - 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)
}
}
}
子数据 Class - ImagesModel
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
的艰苦工作
build.gradle(应用模块)
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 工作室在 class 上显示 describeContents
或 writeToParcel
未实现的错误,即使有 @Parcelize
注释,请检查在您的 app/build.gradle
中您没有启用 apply plugin: 'kotlin-android-extensions'
。
这是行不通的。出于某种原因,我的项目使用旧的已弃用版本的扩展 (ktxVersion = '1.0.2')。
所以这与 @Parcelize
实施相冲突。
当我尝试添加 apply plugin: 'kotlin-parcelize'
时,它说它不能同时使用扩展,我只能使用一个或另一个。
与同事合作后,我们发现扩展导致整个 @Parcelize
失败。我尝试了很多不同的东西,但即使在克服了编译时错误之后,运行时错误也会说“CREATOR”未找到异常或类似的。
所以最后我为 kotlin-android-extensions
删除了 apply plugin 并且只包含 apply plugin: 'kotlin-parcelize'
,这解决了那个问题,它按预期工作。
我目前在我的 Java 项目中使用优秀的 AutoParcel,它有助于创建 Parcelable classes。
现在,我考虑将其用于下一个项目的 Kotlin 具有这种数据概念 classes,它会自动生成 equals、hashCode 和 toString 方法。
有没有方便的方法使 Kotlin 数据 class 以方便的方式可打包(无需手动实现方法)?
不幸的是,Kotlin 无法在接口中放置一个真实的字段,因此您不能免费从接口适配器继承它:
data class Par : MyParcelable
您可能会查看委派,但它对字段没有帮助,AFAIK:https://kotlinlang.org/docs/reference/delegation.html
所以我看到的唯一选择是 Parcelable.Creator
的结构函数,这很明显。
有一个插件,但并不总是随着 Kotlin 的发展而更新: https://plugins.jetbrains.com/plugin/8086
选择:
我有一个使用 Parcelable
和列表的自定义数据 class 的工作示例:
数据 classes 使用带有列表的 Parcelable:
希望对您有所帮助!
你可以试试这个插件:
android-parcelable-intellij-plugin-kotlin
它帮助您为 kotlin 数据 class 生成 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)
}
}
}
}
你试过PaperParcel了吗?它是一个注释处理器,可以自动为您生成 Android Parcelable
样板代码。
用法:
用 @PaperParcel
注释您的数据 class,实施 PaperParcelable
,并添加生成的 CREATOR
的 JVM 静态实例,例如:
@PaperParcel
data class Example(
val test: Int,
...
) : PaperParcelable {
companion object {
@JvmField val CREATOR = PaperParcelExample.CREATOR
}
}
现在你的数据class是Parcelable
,可以直接传递给Bundle
或Intent
编辑:更新最新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) }
然后我像这样创建 parcelables:
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)
完全没有样板代码的最佳方式是Smugglergradle插件。您所需要的只是实现 AutoParcelable 接口,例如
data class Person(val name:String, val age:Int): AutoParcelable
仅此而已。也适用于密封 类。此插件还为所有 AutoParcelable 类 提供编译时验证。
UPD 17.08.2017 现在 Kotlin 1.1.4 and Kotlin Android extensions plugin 您可以使用 @Parcelize
注释。在这种情况下,上面的示例将如下所示:
@Parcelize class Person(val name:String, val age:Int): Parcelable
不需要 data
修饰符。目前最大的缺点是使用 kotlin-android-extensions 插件,它有很多其他可能不需要的功能。
使用 Android Studio 和 Kotlin 插件,我找到了一种简单的方法来转换我的旧 Java Parcelable
s 没有额外的插件(如果你只想把一个全新的 data
class 变成 Parcelable
,跳到第 4 个代码片段)。
假设您有一个 Person
class 和所有 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
实现,留下一个基本的、普通的、旧的 Java 对象(属性应该是最终的并由构造函数):
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
class:
data class Person(val firstName: String, val lastName: String, val age: Int)
最后,让我们再次将其变成 Parcelable
。将鼠标悬停在 class 名称上,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
class 定义。
备注:
- 尝试将 Java
Parcelable
直接转换为 Kotlin 不会产生与Kotlin 插件的当前版本 (1.1.3
). - 我不得不删除当前
Parcelable
代码生成器引入的一些额外的花括号。应该是小bug。
我希望这个技巧对你和我一样有用。
Android 扩展插件现在包含一个自动 Parcelable 实现生成器。在主构造函数中声明序列化属性并添加@Parcelize 注释,writeToParcel()/createFromParcel() 方法将自动创建:
@Parcelize
class User(val firstName: String, val lastName: String) : Parcelable
所以你需要启用他们将这个添加到你模块的 build.gradle:
apply plugin: 'org.jetbrains.kotlin.android.extensions'
android {
androidExtensions {
experimental = true
}
}
只需单击您的 kotlin 数据的数据关键字 class,然后按 alt+Enter,select 第一个选项显示 "Add Parceable Implementation"
Kotlin 通过其 @Parcel 注释使 Android 中的整个 Parcelization 过程非常简单。
这样做
步骤 1. 在您的应用模块中添加 Kotlin 扩展 gradle
第 2 步。 添加实验性 = true,因为此功能在 gradle 中仍在实验中。
androidExtensions { experimental = true }
步骤 3. 使用 @Parcel
注释数据 classHere 是@Parcel 用法的简单示例
- 在模型/数据之上使用 @Parcelize 注释 class
- 使用最新版本的 Kotlin
- 在您的应用模块中使用最新版本的 Kotlin Android 扩展
示例:
@Parcelize
data class Item(
var imageUrl: String,
var title: String,
var description: Category
) : Parcelable
您可以使用 @Parcelize
注释来完成。它是一个自动 Parcelable 实现生成器。
首先,您需要启用它们,将其添加到您模块的 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 代码可以帮助您创建 Parcelable 数据 classes with Parent and Child Data class
父数据Class - 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)
}
}
}
子数据 Class - ImagesModel
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
build.gradle(应用模块)
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 工作室在 class 上显示 describeContents
或 writeToParcel
未实现的错误,即使有 @Parcelize
注释,请检查在您的 app/build.gradle
中您没有启用 apply plugin: 'kotlin-android-extensions'
。
这是行不通的。出于某种原因,我的项目使用旧的已弃用版本的扩展 (ktxVersion = '1.0.2')。
所以这与 @Parcelize
实施相冲突。
当我尝试添加 apply plugin: 'kotlin-parcelize'
时,它说它不能同时使用扩展,我只能使用一个或另一个。
与同事合作后,我们发现扩展导致整个 @Parcelize
失败。我尝试了很多不同的东西,但即使在克服了编译时错误之后,运行时错误也会说“CREATOR”未找到异常或类似的。
所以最后我为 kotlin-android-extensions
删除了 apply plugin 并且只包含 apply plugin: 'kotlin-parcelize'
,这解决了那个问题,它按预期工作。