Есть ли удобный способ создания классов данных Parcelable в Android с помощью Kotlin?

118

В настоящее время я использую превосходный AutoParcel в своем проекте Java, который облегчает создание классов Parcelable.

Теперь в Kotlin, который я рассматриваю для своего следующего проекта, есть концепция классов данных, которые автоматически генерируют методы equals, hashCode и toString.

Есть ли удобный способ сделать класс данных Kotlin Parcelable удобным способом (без реализации методов вручную)?

талесмелло
источник
2
Вы пробовали капт? blog.jetbrains.com/kotlin/2015/06/…
Сергей Машков
Вы хотите использовать AutoParcel с Kotlin? Я думал об этом, но если бы существовал способ встроить классы данных Parcelable в язык, это было бы прекрасно. Особенно учитывая, что большая часть использования Kotlin будет приходиться на приложения Android.
thalesmello

Ответы:

190

Kotlin 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?
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

Вы можете попробовать этот плагин:

Android-Parcelable-Intellij-плагин-Котлин

Он поможет вам сгенерировать шаблонный код 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

Просто щелкните ключевое слово data вашего класса данных 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 больше не может использоваться с классами данных ...
Массимо
Они никогда не могли наследовать от других классов, но они могут реализовать интерфейсы (например, 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вводит текущий генератор кода. Должна быть небольшая ошибка.

Я надеюсь, что этот совет сработает для вас так же хорошо, как и для меня.

Аргенкиви
источник
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, что отчасти очевидно.

voddan
источник
2

я предпочитаю просто использовать https://github.com/johncarl81/parceler lib с

@Parcel(Parcel.Serialization.BEAN)
data class MyClass(val value)
Ян Рабе
источник
Это дает ошибку «Класс MyClass не является абстрактным и не реализует абстрактное общедоступное абстрактное развлечение для членов writeToParcel (dest: Parcel !, flags: Int): Unit, определенный в 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. Добавьте экспериментальное = true, так как эта функция все еще находится в стадии экспериментов в градиенте.

androidExtensions {экспериментальный = true}

Шаг 3. Анонсируйте класс данных с помощью @Parcel

Вот простой пример использования @Parcel

Рамакришна Джоши
источник
0

Существует плагин, но он не всегда обновляется по мере развития Kotlin: https://plugins.jetbrains.com/plugin/8086

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

Классы данных, использующие Parcelable со списками:

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

Надеюсь, поможет!

Хуан Саравиа
источник