是否有使用Kotlin在Android中创建Parcelable数据类的便捷方法?


116

我目前在Java项目中使用了出色的AutoParcel,这有助于创建Parcelable类。

现在,我在下一个项目中考虑的Kotlin具有这种数据类的概念,它可以自动生成equals,hashCode和toString方法。

有没有简便的方法可以使Kotlin数据类可打包(无需手动实现方法)?



您是要在Kotlin中使用AutoParcel?我曾考虑过这一点,但是如果有一种方法可以在语言中内置Parcelable数据类,那就太漂亮了。特别考虑到Kotlin使用的很大一部分将来自Android应用程序。
thalesmello,2015年

Answers:


187

Kotlin 1.1.4已经发布

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
    }
}


3
为什么这不再是数据类。这个例子仅仅是为了表明它可以应用于任何通用的kotlin类吗?
Nitin Jain

10
编译器抱怨this calss implements Parcelable but does not provice CREATOR field。您的答案足够(完整)吗?
莫尔特(Mur)

1
@murt您是否成功使用了Parcelable?由于创建者,我遇到了编译错误
TOP

4
您可以@SuppressLint("ParcelCreator")用来摆脱棉绒警告。
荷兰大师赛

47

您可以尝试以下插件:

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)
            }
        }
    }
}

19

您尝试过PaperParcel吗?这是一个注释处理器,可以自动Parcelable为您生成Android 样板代码。

用法:

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

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

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

编辑:使用最新的API更新


IDE现在显示“禁止从其他类继承数据类”。我想PaperParcel不能与数据类再使用...
马西莫

他们永远无法从其他类继承,但是他们可以实现接口(例如PaperParcelable)
Bradley Campbell

1
@Bradley Campbell这在此行给我一个错误JvmField val CREATOR = PaperParcelExample.CREATOR无法创建PaperParcelExample类
Mr.G

18

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


2
我使用了这种方法,但是有几个问题。有时,它可以代替一个parcel.read...TODO,如果一个字段是不是val/var。如果List在类内部使用,则实现会成为问题。所以我转向@Parcelize了公认的答案。
CoolMind

16

完全没有样板代码的最佳方法是Smuggler gradle插件。您只需要实现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-extensions插件,该插件具有许多其他不必要的功能。


6

使用Android StudioKotlin插件,我发现了一种无需额外插件即可转换旧Java Parcelable的简便方法(如果您只想将全新的类转换为,请跳至第4个代码段)。 dataParcelable

假设您有一个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实现,然后留下一个简朴,简单的旧Java对象(属性应为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. 尝试将Java Parcelable直接转换为Kotlin不会与当前版本的Java产生相同的结果 Kotlin插件(1.1.3)。
  2. 我不得不删除一些额外的花括号当前 Parcelable代码生成器引入的。必须是一个小错误。

希望本技巧对您同样有用。


4

我将离开我的工作方式,以防它可能对某人有所帮助。

我要做的是我有一个通用的 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()) } }
}

这使我摆脱了样板重写。


4
  • 使用@Parcelize在模型/数据类的顶部批注
  • 使用最新版本的Kotlin
  • 在您的应用模块中使用最新版本的Kotlin Android扩展

范例:

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

3

不幸的是,在Kotlin中无法将真实字段放入接口中,因此您不能免费从interface-adapter继承它: data class Par : MyParcelable

您可能会看一下委派,但对AFAIK字段没有帮助: https //kotlinlang.org/docs/reference/delegation.html

因此,我看到的唯一选择是的Fabric函数Parcelable.Creator,这很明显。



2

您可以使用@Parcelize注释来完成。它是一个自动的Parcelable实现生成器。

首先,您需要让他们将其添加到模块的build.gradle中:

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

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

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

DONT需要添加experimental = true内部androidExtensions块。


1

Kotlin的@Parcel批注使Android的Parcelization整个过程变得简单。

要做到这一点

步骤1.在您的应用模块gradle中添加Kotlin扩展

第2步。添加experimental = true,因为此功能仍在gradle中进行实验。

androidExtensions {实验=真}

步骤3.使用 @Parcel注释数据类

是有关@Parcel用法的简单示例


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.