มีวิธีที่สะดวกในการสร้างคลาสข้อมูลแบบ Parcelable ใน Android ด้วย Kotlin หรือไม่


118

ฉันกำลังใช้AutoParcel ที่ยอดเยี่ยมในโปรเจ็กต์ Java ของฉันซึ่งอำนวยความสะดวกในการสร้างคลาส Parcelable

ตอนนี้ Kotlin ซึ่งฉันพิจารณาสำหรับโปรเจ็กต์ถัดไปของฉันมีแนวคิดเกี่ยวกับคลาสข้อมูลนี้ซึ่งสร้างวิธีการเท่ากับแฮชโค้ดและ toString โดยอัตโนมัติ

มีวิธีที่สะดวกในการสร้างคลาสข้อมูล Kotlin แบบ Parcelable ด้วยวิธีที่สะดวก (โดยไม่ต้องใช้วิธีการด้วยตนเอง) หรือไม่?


2
คุณลอง kapt หรือไม่? blog.jetbrains.com/kotlin/2015/06/…
Sergey Mashkov

คุณต้องการใช้ AutoParcel กับ Kotlin หรือไม่? ฉันคิดเกี่ยวกับเรื่องนั้น แต่ถ้ามีวิธีที่จะมีคลาสข้อมูล Parcelable ในภาษามันจะสวยงาม การพิจารณาการใช้งาน Kotlin เป็นพิเศษจะมาจากแอปพลิเคชัน Android
thalesmello

คำตอบ:


189

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

2
สำหรับผู้ที่ต้องการลองดู: blog.jetbrains.com/kotlin/2017/08/kotlin-1-1-4-is-out
thalesmello

3
เหตุใดจึงไม่ใช่ชั้นข้อมูลอีกต่อไป ตัวอย่างนี้เป็นเพียงการแสดงว่าสามารถนำไปใช้กับคลาส kotlin ทั่วไปได้หรือไม่?
Nitin Jain

10
this calss implements Parcelable but does not provice CREATOR fieldคอมไพเลอร์บ่น คำตอบของคุณเพียงพอ (เต็ม) หรือไม่?
murt

1
@murt คุณใช้ Parcelable สำเร็จหรือไม่? ฉันพบข้อผิดพลาดในการคอมไพล์เนื่องจาก CREATOR
TOP

4
คุณสามารถใช้@SuppressLint("ParcelCreator")เพื่อกำจัดคำเตือนผ้าสำลี
Dutch Masters

47

คุณสามารถลองใช้ปลั๊กอินนี้:

หุ่นยนต์ parcelable-IntelliJ-ปลั๊กอิน Kotlin

ช่วยให้คุณสร้างโค้ดสำเร็จรูป Android Parcelable สำหรับคลาสข้อมูลของ kotlin และในที่สุดก็มีลักษณะดังนี้:

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

20

เพียงคลิกที่คีย์เวิร์ดข้อมูลของคลาสข้อมูล kotlin ของคุณจากนั้นกด alt + Enter เลือกตัวเลือกแรกว่า "Add Parceable Implementation"


2
ฉันใช้วิธีนี้ แต่มีปัญหาหลายประการ บางครั้งก็ทดแทนparcel.read...ด้วยถ้าข้อมูลไม่ได้TODO val/varหากคุณใช้Listภายในชั้นเรียนการใช้งานของคุณจะมีปัญหา ฉันจึงหันไป@Parcelizeหาคำตอบที่ได้รับการยอมรับ
CoolMind

19

คุณลองPaperParcel แล้วหรือยัง? เป็นตัวประมวลผลคำอธิบายประกอบที่สร้างParcelableรหัสต้นแบบAndroid ให้คุณโดยอัตโนมัติ

การใช้งาน:

ใส่คำอธิบายประกอบคลาสข้อมูลของคุณด้วย@PaperParcelใช้งานPaperParcelableและเพิ่มอินสแตนซ์แบบคงที่ JVM ของสิ่งที่สร้างขึ้นCREATORเช่น:

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

ตอนนี้คลาสข้อมูลของคุณParcelableและสามารถส่งผ่านไปยัง a BundleหรือIntent

แก้ไข:อัปเดตด้วย API ล่าสุด


ขณะนี้ IDE ระบุว่า "การสืบทอดคลาสข้อมูลจากคลาสอื่นเป็นสิ่งต้องห้าม" ฉันคิดว่า PaperParcel ไม่สามารถใช้กับคลาสข้อมูลได้อีกต่อไป ...
Massimo

พวกเขาไม่สามารถสืบทอดจากคลาสอื่น ๆ ได้ แต่สามารถใช้อินเทอร์เฟซได้ (เช่น PaperParcelable)
Bradley Campbell

1
@Bradley Campbell สิ่งนี้ทำให้ฉันมีข้อผิดพลาดในบรรทัดนี้ JvmField val CREATOR = PaperParcelExample.CREATOR ไม่สามารถสร้างคลาส
PaperParcelExample

16

ที่ดีที่สุดทางกับต้นแบบไม่มีรหัสที่ทุกคนลักลอบปลั๊กอิน 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 ซึ่งมีฟังก์ชันอื่น ๆ อีกมากมายที่อาจไม่จำเป็น


6

เมื่อใช้Android Studioและปลั๊กอินKotlinฉันพบวิธีง่ายๆในการแปลง Java เก่าของฉันParcelableโดยไม่ต้องใช้ปลั๊กอินเพิ่มเติม (หากคุณต้องการเพียงแค่เปลี่ยนdataคลาสใหม่ให้เป็น a 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การนำไปใช้งานทิ้งวัตถุ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คลาส:

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โดยตรงจะไม่ให้ผลลัพธ์เดียวกันกับปลั๊กอินKotlin ( 1.1.3) เวอร์ชันปัจจุบัน
  2. ฉันต้องถอดวงเล็บปีกกาพิเศษที่ตัวสร้างParcelableรหัสปัจจุบันแนะนำ ต้องเป็นบั๊กเล็กน้อย

ฉันหวังว่าเคล็ดลับนี้จะเหมาะกับคุณเช่นเดียวกับที่ทำกับฉัน


5
  • ใช้คำอธิบายประกอบ@Parcelizeที่ด้านบนของคลาส Model / Data ของคุณ
  • ใช้ Kotlin เวอร์ชันล่าสุด
  • ใช้ Kotlin Android Extensions เวอร์ชันล่าสุดในโมดูลแอปของคุณ

ตัวอย่าง:

@Parcelize
data class Item(
    var imageUrl: String,
    var title: String,
    var description: Category
) : 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()) } }
}

ซึ่งทำให้ฉันกำจัดการแทนที่สำเร็จรูปนั้น


3

น่าเสียดายที่ Kotlin ไม่มีวิธีในการใส่ฟิลด์จริงในอินเทอร์เฟซดังนั้นคุณจึงไม่สามารถสืบทอดจากอะแดปเตอร์อินเทอร์เฟซได้ฟรี: data class Par : MyParcelable

คุณอาจดูการมอบสิทธิ์ แต่ไม่ได้ช่วยเรื่องฟิลด์ AFAIK: https://kotlinlang.org/docs/reference/delegation.html

ดังนั้นตัวเลือกเดียวที่ฉันเห็นคือฟังก์ชันผ้าParcelable.Creatorซึ่งเห็นได้ชัด


2

ฉันชอบเพียงแค่ใช้https://github.com/johncarl81/parceler lib กับไฟล์

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

สิ่งนี้ทำให้ข้อผิดพลาด "Class 'MyClass' ไม่เป็นนามธรรมและไม่ได้ใช้งานนามธรรมที่เป็นนามธรรมสำหรับสมาชิกที่เป็นนามธรรม writeToParcel (dest: Parcel !, ธง: Int): หน่วยที่กำหนดใน android.os.Parcelable
PhillyTheThrilly

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

คุณไม่จำเป็นต้องเพิ่มบล็อกexperimental = trueภายในandroidExtensions


1

Kotlin ทำให้กระบวนการทั้งหมดของ Parcelization ใน Android เป็นเรื่องง่ายด้วยคำอธิบายประกอบ @Parcel

การทำเช่นนั้น

ขั้นตอนที่ 1.เพิ่มส่วนขยาย Kotlin ในโมดูลแอปของคุณ gradle

ขั้นตอนที่ 2.เพิ่มการทดลอง = จริงเนื่องจากคุณลักษณะนี้ยังอยู่ในการทดลองในการไล่ระดับสี

androidExtensions {ทดลอง = true}

ขั้นตอนที่ 3. ยกเลิกคลาสข้อมูลด้วย @Parcel

นี่คือตัวอย่างง่ายๆในการใช้งาน @Parcel


0

มีปลั๊กอิน แต่ไม่ได้รับการอัปเดตเสมอไปเนื่องจาก Kotlin กำลังพัฒนาอยู่: https://plugins.jetbrains.com/plugin/8086

ทางเลือก: ฉันมีตัวอย่างการทำงานของคลาสข้อมูลที่กำหนดเองที่ใช้Parcelableและรายการ:

คลาสข้อมูลโดยใช้ Parcelable with Lists:

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

หวังว่าจะช่วยได้!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.