Có cách nào thuận tiện để tạo các lớp dữ liệu Parcelable trong Android với Kotlin không?


117

Tôi hiện đang sử dụng AutoParcel tuyệt vời trong dự án Java của mình, nó tạo điều kiện thuận lợi cho việc tạo các lớp Parcelable.

Bây giờ, Kotlin, mà tôi đang xem xét cho dự án tiếp theo của mình, có khái niệm về các lớp dữ liệu, tự động tạo ra các phương thức bằng, mã băm và toString.

Có cách nào thuận tiện để tạo một lớp dữ liệu Kotlin có thể thành Parcelable một cách thuận tiện (mà không cần triển khai các phương thức theo cách thủ công) không?



Ý bạn là sử dụng AutoParcel với Kotlin? Tôi đã nghĩ về điều đó, nhưng nếu có một cách nào đó để tích hợp các lớp dữ liệu Parcelable vào ngôn ngữ này, thì nó sẽ rất hay. Đặc biệt xem xét một phần lớn việc sử dụng Kotlin sẽ đến từ các ứng dụng Android.
thalesmello

Câu trả lời:


187

Kotlin 1.1.4 đã ra mắt

Plugin Tiện ích mở rộng Android hiện bao gồm trình tạo triển khai có thể phân loại tự động. Khai báo các thuộc tính tuần tự hóa trong một hàm tạo chính và thêm chú thích @Parcelize và các phương thức writeToParcel () / createFromParcel () sẽ được tạo tự động:

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

Vì vậy, bạn cần cho phép họ thêm điều này vào build.gradle của mô-đun :

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

android {
    androidExtensions {
        experimental = true
    }
}

2
Dành cho những ai muốn khám phá: blog.jetbrains.com/kotlin/2017/08/kotlin-1-1-4-is-out
thalesmello

3
tại sao đây không còn là lớp dữ liệu nữa. Có phải ví dụ này chỉ để cho thấy rằng điều này có thể được áp dụng trên bất kỳ lớp kotlin chung nào không?
Nitin Jain

10
trình biên dịch phàn nàn this calss implements Parcelable but does not provice CREATOR field. Câu trả lời của bạn đã đủ (đầy đủ) chưa?
murt

1
@murt Bạn đã sử dụng thành công Parcelable? Tôi đang gặp phải lỗi biên dịch do CREATOR
ĐẦU,

4
Bạn có thể sử dụng @SuppressLint("ParcelCreator")để thoát khỏi cảnh báo xơ vải.
Dutch Masters

47

Bạn có thể thử plugin này:

android-parcelable-intellij-plugin-kotlin

Nó giúp bạn tạo mã soạn sẵn Android Parcelable cho lớp dữ liệu của kotlin. Và cuối cùng nó trông như thế này:

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

Chỉ cần nhấp vào từ khóa dữ liệu của lớp dữ liệu kotlin của bạn, sau đó nhấn alt + Enter, chọn tùy chọn đầu tiên nói "Add Parceable Implementation"


2
Tôi đã sử dụng phương pháp này, nhưng nó có một số vấn đề. Đôi khi nó thay thế parcel.read...bằng TODO, nếu một trường không phải val/var. Nếu bạn sử dụng Listbên trong một lớp, việc triển khai của bạn sẽ trở thành một vấn đề. Vì vậy, tôi đã chuyển sang @Parcelizecâu trả lời được chấp nhận.
CoolMind

19

Bạn đã thử PaperParcel chưa? Đó là một bộ xử lý chú thích tự động tạo Parcelablemã bảng soạn sẵn Android cho bạn.

Sử dụng:

Chú thích lớp dữ liệu của bạn với @PaperParcel, triển khai PaperParcelablevà thêm một phiên bản tĩnh JVM của CREATORví dụ:

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

Bây giờ, lớp dữ liệu của bạn đang Parcelablevà có thể được chuyển trực tiếp đến một BundlehoặcIntent

Chỉnh sửa: Cập nhật với API mới nhất


IDE bây giờ cho biết "Kế thừa lớp dữ liệu từ các lớp khác bị cấm". Tôi nghĩ rằng PaperParcel không thể được sử dụng nữa với các lớp dữ liệu ...
Massimo

Chúng không bao giờ có thể kế thừa từ các lớp khác, nhưng chúng có thể triển khai các giao diện (ví dụ: PaperParcelable)
Bradley Campbell

1
@Bradley Campbell Điều này cho tôi một lỗi trong dòng này JvmField val CREATOR = PaperParcelExample.CREATOR không thể tạo lớp PaperParcelExample
Mr.G

16

Cách tốt nhấtkhông cầnsoạn sẵn là plugin Smuggler gradle. Tất cả những gì bạn cần chỉ là triển khai giao diện AutoParcelable như

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

Và đó là tất cả. Hoạt động cho các lớp kín. Ngoài ra, plugin này cung cấp xác thực thời gian biên dịch cho tất cả các lớp AutoParcelable.

UPD 17.08.2017 Bây giờ với plugin mở rộng Android Kotlin 1.1.4 và Kotlin, bạn có thể sử dụng @Parcelizechú thích. Trong trường hợp này, ví dụ trên sẽ giống như sau:

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

Không cần databổ ngữ. Nhược điểm lớn nhất hiện tại là sử dụng plugin kotlin-android-extensions có rất nhiều chức năng khác có thể không cần thiết.


6

Sử dụng Android Studio và plugin Kotlin , tôi đã tìm thấy một cách dễ dàng để chuyển đổi các Java cũ Parcelablecủa mình mà không cần bổ sung thêm plugin (nếu tất cả những gì bạn muốn là biến một datalớp hoàn toàn mới thànhParcelable , hãy chuyển đến đoạn mã thứ 4).

Giả sử bạn có một Personlớp với tất cả các Parcelabletấm lò hơi:

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

Bắt đầu bằng cách loại bỏ việc Parcelabletriển khai, để lại một đối tượng Java cũ, đơn giản, (các thuộc tính phải là cuối cùng và được thiết lập bởi hàm tạo):

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

Sau đó, hãy để Code > Convert Java file to Kotlin Filetùy chọn làm điều kỳ diệu của nó:

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

Chuyển đổi này thành một datalớp:

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

Và cuối cùng, hãy biến điều này thành một Parcelablelần nữa. Di chuột qua tên lớp và Android Studio sẽ cung cấp cho bạn tùy chọn Add Parcelable Implementation. Kết quả sẽ như thế này:

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

Như bạn có thể thấy, việc Parcelabletriển khai là một số mã được tạo tự động được thêm vào datađịnh nghĩa lớp của bạn .

Ghi chú:

  1. Việc cố gắng chuyển đổi Java Parcelable trực tiếp thành Kotlin sẽ không tạo ra kết quả tương tự với phiên bản hiện tại của plugin Kotlin ( 1.1.3).
  2. Tôi đã phải loại bỏ một số dấu ngoặc nhọn bổ sung mà Parcelabletrình tạo mã hiện tại giới thiệu. Phải là một lỗi nhỏ.

Tôi hy vọng mẹo này hiệu quả với bạn cũng như nó đã làm cho tôi.


4

Tôi sẽ để lại cách làm của mình trong trường hợp nó có thể giúp ích cho ai đó.

Những gì tôi làm là tôi có một thứ chung chung 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) }

Và sau đó tôi tạo các bưu kiện như thế này:

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

Điều đó giúp tôi loại bỏ việc ghi đè bảng soạn sẵn đó.


4
  • Sử dụng chú thích @Parcelize ở đầu lớp Mô hình / Dữ liệu của bạn
  • Sử dụng phiên bản mới nhất của Kotlin
  • Sử dụng phiên bản mới nhất của Kotlin Android Extensions trong mô-đun ứng dụng của bạn

Thí dụ :

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

3

Thật không may, trong Kotlin không có cách nào để đưa một trường thực vào một giao diện, vì vậy bạn không thể kế thừa nó miễn phí từ bộ điều hợp giao diện: data class Par : MyParcelable

Bạn có thể xem xét ủy quyền, nhưng nó không giúp ích gì cho các trường, AFAIK: https://kotlinlang.org/docs/reference/delegation.html

Vì vậy, lựa chọn duy nhất tôi thấy là một chức năng vải Parcelable.Creator, đó là một điều hiển nhiên.


2

tôi chỉ thích sử dụng https://github.com/johncarl81/parceler lib với

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

Điều này gây ra lỗi "Lớp 'MyClass' không trừu tượng và không triển khai thành viên trừu tượng công khai trừu tượng vui vẻ writeToParcel (dest: Parcel !, flags: Int): Đơn vị được định nghĩa trong android.os.Parcelable
PhillyTheThrilly

2

Bạn có thể làm điều đó bằng cách sử dụng @Parcelizechú thích. Nó là một trình tạo triển khai Parcelable tự động.

Trước tiên, bạn cần kích hoạt họ thêm điều này vào build.gradle của mô-đun của bạn:

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

Khai báo các thuộc tính tuần tự hóa trong một hàm tạo chính và thêm @Parcelizechú thích, và writeToParcel()/ createFromParcel()các phương thức sẽ được tạo tự động:

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

Bạn KHÔNG cần thêm khối experimental = truebên trong androidExtensions.


1

Kotlin đã làm cho toàn bộ quá trình Chia nhỏ trong Android trở nên dễ dàng hơn với chú thích @Parcel.

Để làm việc đó

Bước 1. Thêm tiện ích mở rộng Kotlin trong gradle mô-đun ứng dụng của bạn

Bước 2. Thêm thử nghiệm = true vì tính năng này vẫn đang trong quá trình thử nghiệm trong gradle.

androidExtensions {thử nghiệm = true}

Bước 3. Thông báo lớp dữ liệu với @Parcel

Đây là một ví dụ đơn giản về cách sử dụng @Parcel


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.