Чи є зручний спосіб створити класи даних Parcelable в Android з Kotlin?


117

Наразі я використовую відмінний AutoParcel у своєму проекті Java, який полегшує створення класів Parcelable.

Тепер у Котліна, який я вважаю для свого наступного проекту, є ця концепція класів даних, які автоматично генерують методи рівності, hashCode та toString.

Чи є зручний спосіб зробити клас даних Kotlin Parcelable зручним способом (без реалізації методів вручну)?



Ви маєте на увазі використовувати AutoParcel з Kotlin? Я думав про це, але якби був спосіб, щоб класи даних Parcelable були вбудовані в мову, було б прекрасно. Особливо враховуючи, що величезна частина використання Kotlin буде надходити з додатків Android.
thalesmello

Відповіді:


187

Котлін 1.1.4 вийшов

Тепер плагін Android Extensions включає автоматичний генератор реалізації 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?
Нітін Джайн

10
компілятор скаржиться this calss implements Parcelable but does not provice CREATOR field. Ваша відповідь достатня (повна)?
murt

1
@murt Ви успішно використовували Parcelable? Я зіткнувся з помилкою компіляції через CREATOR
TOP

4
Ви можете використовувати @SuppressLint("ParcelCreator")для позбавлення від попередження про ворсинки.
Голландські майстри

47

Ви можете спробувати цей плагін:

android-parcelable-intellij-plugin-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
Я використовував цей метод, але він має кілька проблем. Іноді він підміняє a 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і може бути переданий безпосередньо до BundleабоIntent

Редагування: оновлення за допомогою останнього API


Тепер IDE каже, що "успадкування класів даних від інших класів заборонено". Я думаю, що PaperParcel більше не можна використовувати з класами даних ...
Massimo,

Вони ніколи не могли успадкувати інші класи, але вони можуть реалізовувати інтерфейси (наприклад, PaperParcelable)
Бредлі Кемпбелл

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-extensions, який має безліч інших функцій, які можуть бути непотрібними.


6

Використання Android Studio і Котлин плагін, я знайшов простий спосіб конвертувати мій старий Java Parcelables з яких - небудь додаткових плагінів (якщо все , що ви хочете, щоб перетворити новий dataклас в 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вводить генератор поточного коду. Повинно бути незначною помилкою.

Я сподіваюся, що ця порада спрацює для вас так само, як і для мене.


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 анотацію над своїм класом Модель / Дані
  • Використовуйте останню версію Котліна
  • Використовуйте останню версію розширень Android від Kotlin у своєму модулі додатків

Приклад:

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

3

На жаль, у Котліна немає можливості розмістити реальне поле в інтерфейсі, тому ви не можете його успадкувати від інтерфейсу-адаптера: 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)

Це дає помилку "Клас" 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

Котлін спростив увесь процес парцелізації в Android проклятим своїм анотацією @Parcel.

Щоб це зробити

Крок 1. Додайте розширення Kotlin у ступінь модуля додатка

Крок 2. Додайте експериментальний = вірно, оскільки ця функція все ще знаходиться в експерименті на градусах.

androidExtensions {експериментальний = справжній}

Крок 3. Позначте клас даних за допомогою @Parcel

Ось простий приклад використання @Parcel


0

Є плагін, але він не завжди є таким оновленим, як розвивається Котлін: https://plugins.jetbrains.com/plugin/8086

Альтернатива: у мене є робочий приклад користувальницького класу даних із використанням Parcelableсписків:

Класи даних за допомогою Parcelable with Lists:

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

Сподіваюся, це допомагає!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.