Apakah ada cara mudah untuk membuat kelas data Parcelable di Android dengan Kotlin?

117

Saat ini saya menggunakan AutoParcel yang sangat baik dalam proyek Java saya, yang memfasilitasi pembuatan kelas Parcelable.

Sekarang, Kotlin, yang saya pertimbangkan untuk proyek saya berikutnya, memiliki konsep kelas data ini, yang secara otomatis menghasilkan metode equals, hashCode dan toString.

Apakah ada cara yang mudah untuk membuat kelas data Kotlin Parcelable dengan cara yang nyaman (tanpa mengimplementasikan metode secara manual)?

thalesmello.dll
sumber
2
Apakah Anda mencoba kapt? blog.jetbrains.com/kotlin/2015/06/…
Sergey Mashkov
Anda bermaksud menggunakan AutoParcel dengan Kotlin? Saya memikirkan hal itu, tetapi jika ada cara untuk memiliki kelas data Parcelable yang dimasukkan ke dalam bahasa, itu akan indah. Secara khusus mengingat sebagian besar penggunaan Kotlin akan datang dari aplikasi Android.
thalesmello

Jawaban:

188

Kotlin 1.1.4 sudah keluar

Plugin Android Extensions sekarang menyertakan generator implementasi Parcelable otomatis. Deklarasikan properti berseri dalam konstruktor utama dan tambahkan anotasi @Parcelize, dan metode writeToParcel () / createFromParcel () akan dibuat secara otomatis:

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

Jadi, Anda perlu mengaktifkan mereka dengan menambahkan ini ke modul Anda build.gradle :

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

android {
    androidExtensions {
        experimental = true
    }
}
Dhaval Jivani
sumber
2
Bagi mereka yang ingin memeriksanya: blog.jetbrains.com/kotlin/2017/08/kotlin-1-1-4-is-out
thalesmello
3
mengapa ini bukan lagi kelas data. Apakah contoh ini hanya untuk menunjukkan bahwa ini dapat diterapkan pada kelas kotlin generik?
Nitin Jain
10
kompiler mengeluh this calss implements Parcelable but does not provice CREATOR field. Apakah jawaban Anda cukup (lengkap)?
murt
1
@murt Apakah Anda berhasil menggunakan Parcelable? Saya menghadapi kesalahan kompilasi karena CREATOR
TOP
4
Anda dapat menggunakan @SuppressLint("ParcelCreator")untuk menghilangkan peringatan lint.
Dutch Masters
47

Anda dapat mencoba plugin ini:

android-parcelable-intellij-plugin-kotlin

Ini membantu Anda menghasilkan kode boilerplate Parcelable Android untuk kelas data kotlin. Dan akhirnya terlihat seperti ini:

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)
            }
        }
    }
}
nekocode.dll
sumber
20

Cukup klik kata kunci data kelas data kotlin Anda, lalu tekan alt + Enter, pilih opsi pertama yang bertuliskan "Add Parceable Implementation"

Manish Patiyal
sumber
2
Saya menggunakan metode ini, tetapi ada beberapa masalah. Terkadang itu menggantikan parcel.read...dengan TODO, jika bidang tidak val/var. Jika Anda menggunakan Listdi dalam kelas, implementasi Anda menjadi masalah. Jadi saya beralih ke @Parcelizejawaban yang diterima.
CoolMind
19

Sudahkah Anda mencoba PaperParcel ? Ini adalah pemroses anotasi yang secara otomatis menghasilkan Parcelablekode boilerplate Android untuk Anda.

Pemakaian:

Beri anotasi kelas data Anda dengan @PaperParcel, implementasikan PaperParcelable, dan tambahkan instance statis JVM dari contoh yang dihasilkan CREATOR:

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

Sekarang kelas data Anda adalah Parcelabledan dapat diteruskan langsung ke BundleatauIntent

Edit: Perbarui dengan API terbaru

Bradley Campbell
sumber
IDE sekarang mengatakan "Warisan kelas data dari kelas lain dilarang". Saya pikir PaperParcel tidak dapat digunakan lagi dengan kelas data ...
Massimo
Mereka tidak pernah bisa mewarisi dari kelas lain, tetapi mereka dapat mengimplementasikan antarmuka (misalnya PaperParcelable)
Bradley Campbell
1
@Bradley Campbell Ini memberi saya kesalahan di baris ini JvmField val CREATOR = PaperParcelExample.CREATOR tidak dapat membuat kelas
PaperParcelExample
16

The terbaik cara dengan tidak ada boilerplate kode sama sekali adalah penyelundup Gradle Plugin. Yang Anda butuhkan hanyalah mengimplementasikan antarmuka AutoParcelable seperti

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

Dan itu saja. Berfungsi untuk kelas tertutup juga. Juga plugin ini menyediakan validasi waktu kompilasi untuk semua kelas AutoParcelable.

UPD 17.08.2017 Sekarang dengan plugin ekstensi Kotlin 1.1.4 dan Kotlin Android Anda dapat menggunakan @Parcelizeanotasi. Dalam hal ini, contoh di atas akan terlihat seperti ini:

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

Tidak perlu datapengubah. Kelemahan terbesar, untuk saat ini, menggunakan plugin kotlin-android-extensions yang memiliki banyak fungsi lain yang mungkin tidak diperlukan.

Stepango
sumber
6

Menggunakan Android Studio dan Kotlin Plugin, saya menemukan cara mudah untuk mengkonversi lama saya Java Parcelables dengan tidak ada plugin tambahan (jika semua yang Anda inginkan adalah untuk mengubah merek baru datakelas menjadi Parcelable, melompat ke potongan kode 4).

Katakanlah Anda memiliki Personkelas dengan semua Parcelablepelat ketel:

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

Mulailah dengan menghapus Parcelableimplementasi, meninggalkan objek Java lama, polos, dan polos (properti harus final dan disetel oleh konstruktor):

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

Kemudian biarkan Code > Convert Java file to Kotlin Fileopsi melakukan keajaibannya:

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

Ubah ini menjadi datakelas:

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

Dan akhirnya, mari kita ubah ini menjadi Parcelablelagi. Arahkan kursor ke nama kelas dan Android Studio akan memberi Anda opsi untuk Add Parcelable Implementation. Hasilnya akan terlihat seperti ini:

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

Seperti yang Anda lihat, Parcelableimplementasinya adalah beberapa kode yang dibuat secara otomatis yang ditambahkan ke datadefinisi kelas Anda .

Catatan:

  1. Mencoba mengonversi Java Parcelable secara langsung ke Kotlin tidak akan menghasilkan hasil yang sama dengan versi plugin Kotlin ( 1.1.3) saat ini.
  2. Saya harus menghapus beberapa tanda kurung kurawal ekstra yang Parcelablediperkenalkan oleh pembuat kode saat ini . Pasti ada bug minor.

Saya harap tip ini bekerja untuk Anda dan juga untuk saya.

argenkiwi
sumber
4

Saya akan meninggalkan cara saya melakukannya jika itu dapat membantu seseorang.

Apa yang saya lakukan adalah saya memiliki generik 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) }

Dan kemudian saya membuat parcelable seperti ini:

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

Yang membuat saya menyingkirkan override boilerplate itu.

gmemario.dll
sumber
4
  • Gunakan anotasi @Parcelize di atas kelas Model / Data Anda
  • Gunakan Kotlin versi terbaru
  • Gunakan versi terbaru Ekstensi Android Kotlin dalam modul aplikasi Anda

Contoh:

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

Sayangnya tidak ada cara di Kotlin untuk menempatkan bidang nyata dalam sebuah antarmuka, jadi Anda tidak dapat mewarisinya dari adaptor antarmuka secara gratis: data class Par : MyParcelable

Anda dapat melihat delegasi, tetapi tidak membantu dengan bidang, AFAIK: https://kotlinlang.org/docs/reference/delegation.html

Jadi satu-satunya pilihan yang saya lihat adalah fungsi kain Parcelable.Creator, yang cukup jelas.

voddan
sumber
2

saya lebih suka hanya menggunakan https://github.com/johncarl81/parceler lib dengan

@Parcel(Parcel.Serialization.BEAN)
data class MyClass(val value)
Jan Rabe
sumber
Ini memberikan kesalahan "Kelas 'Kelas Saya' tidak abstrak dan tidak menerapkan abstrak anggota publik menyenangkan writeToParcel (tujuan: Parcel !, flags: Int): Unit didefinisikan di android.os.Parcelable
PhillyTheThrilly
2

Anda dapat melakukannya dengan menggunakan @Parcelizeanotasi. Ini adalah generator implementasi Parcelable otomatis.

Pertama, Anda harus mengaktifkan mereka untuk menambahkan ini ke modul Anda build.gradle:

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

Deklarasikan properti berseri dalam konstruktor utama dan tambahkan @Parcelizeanotasi, dan writeToParcel()/ createFromParcel()metode akan dibuat secara otomatis:

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

Anda TIDAK perlu menambahkan blok experimental = truedalam androidExtensions.

Malwinder Singh
sumber
1

Kotlin telah membuat seluruh proses Parcelization di Android menjadi sangat mudah dengan anotasi @Parcel-nya.

Untuk melakukannya

Langkah 1. Tambahkan ekstensi Kotlin di gradle modul aplikasi Anda

Langkah 2. Tambahkan eksperimental = true karena fitur ini masih dalam percobaan di gradle.

androidExtensions {eksperimental = true}

Langkah 3. Anonasi kelas data dengan @Parcel

Berikut adalah contoh sederhana tentang penggunaan @Parcel

Ramakrishna Joshi
sumber