Przejdź do treści
School IT / Przestrzeń do nauki

school-it.pl // android + kotlin // rozdział 8 // lekcja 32

Intencja z przekazaniem obiektu

W poprzedniej lekcji drugi ekran dostawał pojedyncze wartości: napis, liczbę, true albo false. Gdy jednak przekazujesz książkę, ucznia czy produkt, takich wartości jest kilka, a każda wymaga osobnego putExtra i osobnego odczytu. Prościej wysłać cały obiekt naraz. W tej lekcji spakujesz obiekt własnej klasy do intencji, odbierzesz go na drugim ekranie i poznasz dwa sposoby pakowania: Serializable i Parcelable.

Kotlin XML Intent putExtra Serializable Parcelable data class 2 × 45 min
CEL LEKCJI

Czego się dziś nauczysz

  • Napiszesz klasę danych, którą da się przesłać intencją (Serializable).
  • Wyślesz obiekt na drugi ekran jednym putExtra.
  • Odbierzesz obiekt i bezpiecznie obsłużysz sytuację, gdy go brakuje.
  • Zrozumiesz, dlaczego drugi ekran dostaje kopię, a nie oryginał.
  • Zamienisz Serializable na szybszy Parcelable z adnotacją @Parcelize.
TEORIA

Po co przekazywać cały obiekt

Książka ma tytuł, autora, rok wydania i informację, czy jest dostępna. Metodą z lekcji 31 trzeba by wysłać cztery wartości i cztery odczytać — a każda literówka w kluczu to puste pole na drugim ekranie:

tak było — cztery wartości osobno
intencja.putExtra("tytul", ksiazka.tytul)
intencja.putExtra("autor", ksiazka.autor)
intencja.putExtra("rok", ksiazka.rok)
intencja.putExtra("dostepna", ksiazka.dostepna)
tak będzie — jeden obiekt
intencja.putExtra("ksiazka", ksiazka)

Gdy klasa dostanie nowe pole, na przykład liczbę stron, pierwszy sposób wymaga zmian w dwóch aktywnościach. Drugi — tylko w klasie i w miejscu, gdzie pole jest wyświetlane.

TEORIA

Serializable — obiekt w paczce

Ekrany aplikacji nie mogą sobie po prostu „podać” obiektu z ręki do ręki. Intencja przechodzi przez system Android, a system przenosi tylko bajty. Obiekt trzeba więc rozłożyć na bajty, przesłać i złożyć z powrotem po drugiej stronie. Działa to jak przeprowadzka szafy: rozkręcasz ją na deski, przewozisz i skręcasz w nowym mieszkaniu.

MainActivity
obiekt Ksiazka
putExtra(„ksiazka”, …)
→
system Android
obiekt rozłożony
na bajty
→
SzczegolyActivity
nowy obiekt Ksiazka
złożony z bajtów

Rozkładanie na bajty nazywa się serializacją. Klasa zgadza się na nią, gdy po dwukropku dopiszesz interfejs Serializable z Javy. Nic więcej nie trzeba — resztę robi Java:

Ksiazka.kt
import java.io.Serializable

data class Ksiazka(
    val tytul: String,
    val autor: String,
    val rok: Int,
    val dostepna: Boolean
) : Serializable

data class to klasa przeznaczona do przechowywania danych — Kotlin sam dopisuje jej metody porównywania i kopiowania. Wszystkie jej pola też muszą dać się serializować. String, Int, Double, Boolean i listy takich wartości — tak. Kontrolki (TextView, Button) i obrazki Bitmap — nie.

TEORIA

Odbieranie obiektu

Drugi ekran wyjmuje obiekt metodą getSerializableExtra. Ta metoda nie wie, jakiej klasy obiekt był w paczce — zwraca ogólny typ Serializable. Trzeba więc powiedzieć Kotlinowi „to jest Ksiazka” operatorem as?:

SzczegolyActivity.kt — fragment
@Suppress("DEPRECATION")
val ksiazka = intent.getSerializableExtra("ksiazka") as? Ksiazka

if (ksiazka == null) {          // brak obiektu albo obiekt innej klasy
    finish()
    return
}
tvTytul.text = ksiazka.tytul    // tu Kotlin już wie, że to Ksiazka
FragmentZnaczenie
intentIntencja, która otworzyła ten ekran. Ma ją każda aktywność.
getSerializableExtra("ksiazka")Wyjmuje obiekt zapisany pod kluczem "ksiazka". Gdy go nie ma — null.
as? Ksiazka„Potraktuj to jako Ksiazka, a jeśli się nie da — daj null”. Wersja bez znaku zapytania (as) zamknęłaby aplikację.
finish() + returnBez danych nie ma czego pokazać — zamykamy ekran i kończymy onCreate.

Dlaczego @Suppress(„DEPRECATION”)

Od Androida 13 (API 33) jest nowsza wersja tej metody: getSerializableExtra("ksiazka", Ksiazka::class.java). Nie działa jednak na starszych telefonach, a nasz projekt obsługuje je od API 24. Dlatego używamy starej wersji. Android Studio przekreśla ją jako przestarzałą (deprecated) — działa poprawnie, a adnotacja @Suppress("DEPRECATION") tylko wycisza to ostrzeżenie.

TEORIA

Drugi ekran dostaje kopię

Skoro obiekt został rozłożony i złożony od nowa, drugi ekran ma inny obiekt z takimi samymi danymi. Zmiana na drugim ekranie nie zmieni książki na liście w MainActivity. To zabezpieczenie: ekran szczegółów nie zepsuje danych listy przez pomyłkę.

Gdy drugi ekran ma zmienić dane (na przykład oznaczyć książkę jako wypożyczoną), musi odesłać nowy obiekt z powrotem, a pierwszy ekran sam podmieni go na liście. Jak odebrać wynik z drugiego ekranu — w lekcji 33.

Paczka ma limit wielkości

Intencja nie jest magazynem. Wysyłaj obiekty z kilkoma polami albo krótkie listy. Setki elementów lub zdjęcia w obiekcie mogą przekroczyć limit (około 1 MB na wszystkie przesyłane w danej chwili dane) i aplikacja zamknie się z błędem TransactionTooLargeException.

PRZYKŁAD

Biblioteka szkolna

Pierwszy ekran pokazuje listę książek z biblioteki — tak jak w lekcji 25, z wbudowanym wierszem simple_list_item_1. W wierszu widać tytuł i autora, bo ArrayAdapter wywołuje na każdym obiekcie toString(). Dotknięcie książki otwiera ekran szczegółów, który dostaje cały obiekt i pokazuje wszystkie jego pola, a status dostępności koloruje na zielono albo czerwono.

9:41
Biblioteka szkolna
Lalka — Bolesław Prus
Pan Tadeusz — Adam Mickiewicz
Quo vadis — Henryk Sienkiewicz
Ferdydurke — Witold Gombrowicz
Solaris — Stanisław Lem
Zbrodnia i kara — Fiodor Dostojewski
MainActivity — lista obiektów Ksiazka
→Intent
+ obiekt
Ksiazka
9:41
Quo vadis
Autor: Henryk Sienkiewicz
Rok wydania: 1896
Dostępna — możesz wypożyczyć
Wróć do listy
SzczegolyActivity — pola odebranego obiektu
9:42
Solaris
Autor: Stanisław Lem
Rok wydania: 1961
Wypożyczona
Wróć do listy
ta sama aktywność dla innej książki

jeden ekran szczegółów obsługuje każdą książkę — różni się tylko obiekt przesłany w intencji

Przygotowanie projektu

  1. Nowy projekt: Empty Views Activity, nazwa Biblioteka, język Kotlin, minimalne SDK API 24.
  2. Klasa danych: prawy przycisk na pakiecie (folder z MainActivity) → New → Kotlin Class/File → nazwa Ksiazka, typ Data class.
  3. Drugi ekran: prawy przycisk na pakiecie → New → Activity → Empty Views Activity → nazwa SzczegolyActivity. Android Studio utworzy plik Kotlin, plik activity_szczegoly.xml i sam dopisze aktywność do AndroidManifest.xml.
  4. Podmień zawartość wszystkich plików na kod poniżej (linie package … zostaw z twojego projektu).
app/src/main/
├── AndroidManifest.xmlwpis SzczegolyActivity — dodaje Android Studio
├── java/…/
│   ├── Ksiazka.ktklasa danych — to ją przesyłamy
│   ├── MainActivity.ktlista książek, wysyła obiekt
│   └── SzczegolyActivity.ktodbiera obiekt
└── res/layout/
    ├── activity_main.xml
    └── activity_szczegoly.xml
AndroidManifest.xml — fragment wewnątrz <application>
<!-- drugi ekran: exported="false" — mogą go otwierać tylko ekrany tej aplikacji -->
<activity
    android:name=".SzczegolyActivity"
    android:exported="false" />
Ksiazka.kt — pełny plik
package pl.schoolit.biblioteka

import java.io.Serializable

// Serializable: obiekt można spakować do intencji
data class Ksiazka(
    val tytul: String,
    val autor: String,
    val rok: Int,
    val dostepna: Boolean
) : Serializable {

    // ten tekst ArrayAdapter pokaże w wierszu listy
    override fun toString(): String {
        return "$tytul — $autor"
    }
}
res/layout/activity_main.xml — pełny plik
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:layout_margin="16dp"
    android:fitsSystemWindows="true">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:text="Biblioteka szkolna"
        android:textSize="24sp"
        android:textStyle="bold" />

    <ListView
        android:id="@+id/lista"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

</LinearLayout>
MainActivity.kt — pełny plik
package pl.schoolit.biblioteka

import android.content.Intent
import android.os.Bundle
import android.widget.ArrayAdapter
import android.widget.ListView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

    private lateinit var lista: ListView

    private val ksiazki = listOf(
        Ksiazka("Lalka", "Bolesław Prus", 1890, true),
        Ksiazka("Pan Tadeusz", "Adam Mickiewicz", 1834, false),
        Ksiazka("Quo vadis", "Henryk Sienkiewicz", 1896, true),
        Ksiazka("Ferdydurke", "Witold Gombrowicz", 1937, true),
        Ksiazka("Solaris", "Stanisław Lem", 1961, false),
        Ksiazka("Zbrodnia i kara", "Fiodor Dostojewski", 1866, true)
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initUI()

        // lista obiektów — w wierszu pojawi się wynik toString()
        lista.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, ksiazki)

        lista.setOnItemClickListener { _, _, position, _ ->
            val ksiazka = ksiazki[position]

            val intencja = Intent(this, SzczegolyActivity::class.java)
            intencja.putExtra("ksiazka", ksiazka)     // cały obiekt pod jednym kluczem
            startActivity(intencja)
        }
    }

    private fun initUI() {
        lista = findViewById(R.id.lista)
    }
}
res/layout/activity_szczegoly.xml — pełny plik
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:layout_margin="16dp"
    android:fitsSystemWindows="true">

    <TextView
        android:id="@+id/tvTytul"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="28sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/tvAutor"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/tvRok"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/tvStatus"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:textSize="18sp"
        android:textStyle="bold" />

    <Button
        android:id="@+id/btnWroc"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="Wróć do listy" />

</LinearLayout>
SzczegolyActivity.kt — pełny plik
package pl.schoolit.biblioteka

import android.graphics.Color
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity

class SzczegolyActivity : AppCompatActivity() {

    private lateinit var tvTytul: TextView
    private lateinit var tvAutor: TextView
    private lateinit var tvRok: TextView
    private lateinit var tvStatus: TextView
    private lateinit var btnWroc: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_szczegoly)
        initUI()

        // wyjmujemy obiekt z intencji, która otworzyła ten ekran
        @Suppress("DEPRECATION")
        val ksiazka = intent.getSerializableExtra("ksiazka") as? Ksiazka

        if (ksiazka == null) {
            Toast.makeText(this, "Brak danych książki", Toast.LENGTH_SHORT).show()
            finish()
            return
        }

        tvTytul.text = ksiazka.tytul
        tvAutor.text = "Autor: ${ksiazka.autor}"
        tvRok.text = "Rok wydania: ${ksiazka.rok}"

        if (ksiazka.dostepna) {
            tvStatus.text = "Dostępna — możesz wypożyczyć"
            tvStatus.setTextColor(Color.parseColor("#2E7D32"))
        } else {
            tvStatus.text = "Wypożyczona"
            tvStatus.setTextColor(Color.parseColor("#C62828"))
        }

        btnWroc.setOnClickListener {
            finish()            // zamyka ten ekran — wraca lista
        }
    }

    private fun initUI() {
        tvTytul = findViewById(R.id.tvTytul)
        tvAutor = findViewById(R.id.tvAutor)
        tvRok = findViewById(R.id.tvRok)
        tvStatus = findViewById(R.id.tvStatus)
        btnWroc = findViewById(R.id.btnWroc)
    }
}

Co dzieje się po kolei

  • Ksiazka to data class z dopiskiem : Serializable — dzięki temu putExtra przyjmie obiekt. Metoda toString() decyduje, co widać w wierszu listy.
  • MainActivity trzyma listę obiektów i podaje ją do ArrayAdapter z wbudowanym wierszem — zupełnie jak napisy w lekcji 25.
  • Po kliknięciu ksiazki[position] daje kliknięty obiekt. Intencja wskazuje klasę drugiego ekranu, putExtra("ksiazka", ksiazka) pakuje obiekt, startActivity otwiera ekran.
  • SzczegolyActivity w onCreate wyjmuje obiekt tym samym kluczem "ksiazka" i zamienia go na Ksiazka przez as?.
  • Gdy obiektu nie ma, ekran pokazuje Toast i zamyka się. Gdy jest — wypełnia pola i koloruje status.
  • finish() zamyka ekran szczegółów. Lista nie była zamknięta — czekała pod spodem, więc po prostu znów ją widać.

Sprawdź trzy rzeczy

  1. Otwórz kilka książek po kolei i wracaj przyciskiem Wróć do listy albo systemowym przyciskiem Wstecz. Za każdym razem wracasz do tej samej listy — ekrany się nie mnożą.
  2. Zmień w MainActivity klucz na "Ksiazka" (wielka litera). Po kliknięciu pojawi się „Brak danych książki” — klucze muszą być identyczne co do znaku.
  3. Usuń z klasy : Serializable. Linia z putExtra podświetli się na czerwono — kompilator nie znajdzie wersji putExtra, która przyjmuje zwykły obiekt.
TEORIA

Parcelable — wersja androidowa

Serializable pochodzi z Javy i działa wszędzie, ale jest wolniejszy, bo w trakcie działania aplikacji sam „bada”, z czego składa się obiekt. Android ma własny, szybszy sposób pakowania: Parcelable (od parcel — paczka). Dawniej wymagał pisania kilkudziesięciu linii kodu, dziś wystarczy adnotacja @Parcelize, a resztę dopisze Kotlin.

Krok 1. W pliku build.gradle.kts (Module :app) dopisz jedną linię w sekcji plugins i kliknij Sync Now. Pozostałe linie zostaw takie, jakie są w twoim projekcie:

build.gradle.kts (Module :app) — fragment
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    id("kotlin-parcelize")          // nowa linia
}

Krok 2. W klasie zamień Serializable na Parcelable i dodaj adnotację:

Ksiazka.kt — wersja Parcelable
import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
data class Ksiazka(
    val tytul: String,
    val autor: String,
    val rok: Int,
    val dostepna: Boolean
) : Parcelable {

    override fun toString(): String {
        return "$tytul — $autor"
    }
}

Krok 3. Wysyłanie się nie zmienia — putExtra("ksiazka", ksiazka) sam wybierze wersję dla Parcelable. Zmienia się odbiór:

SzczegolyActivity.kt — fragment
@Suppress("DEPRECATION")
val ksiazka = intent.getParcelableExtra<Ksiazka>("ksiazka")

Tu nie trzeba as? — typ podajesz w nawiasach ostrych <Ksiazka>. Wynik i tak może być null, więc sprawdzenie if (ksiazka == null) zostaje.

SerializableParcelable + @Parcelize
pochodzenieJava, java.ioAndroid, android.os
przygotowanietylko : Serializablewtyczka w Gradle + adnotacja + : Parcelable
odbiórgetSerializableExtra(…) as? KlasagetParcelableExtra<Klasa>(…)
szybkośćwolniejszyszybszy — zalecany przez Androida
kiedymałe obiekty, nauka, szybki prototypdocelowe aplikacje, listy obiektów

Gdy Gradle nie zna wtyczki

Jeżeli po Sync Now pojawi się błąd Plugin [id: 'kotlin-parcelize’] was not found, twój projekt ma inaczej ustawiony Kotlin. Wtedy w tej samej sekcji użyj pełnej nazwy z wersją Kotlina z pliku gradle/libs.versions.toml (pozycja kotlin = "…"): id("org.jetbrains.kotlin.plugin.parcelize") version "WERSJA".

ELEMENTY WBUDOWANE

Zestawienie elementów

ElementTypCo robi
data class Nazwa(val …)klasaKlasa do przechowywania danych.
: Serializableinterfejs JavyPozwala rozłożyć obiekt na bajty i przesłać go intencją.
Intent(this, Klasa::class.java)obiektIntencja otwierająca wskazany ekran.
putExtra("klucz", obiekt)metodaDokłada obiekt do intencji pod kluczem.
startActivity(intencja)metodaOtwiera ekran opisany intencją.
intentIntentW aktywności: intencja, która ją otworzyła.
getSerializableExtra("klucz")Serializable?Wyjmuje obiekt; null, gdy go brak.
as? KlasaoperatorBezpieczna zmiana typu — null zamiast awarii.
@Parcelize + : ParcelableadnotacjaKotlin sam pisze kod pakowania obiektu.
getParcelableExtra<Klasa>("klucz")Klasa?Wyjmuje obiekt Parcelable.
@Suppress("DEPRECATION")adnotacjaWycisza ostrzeżenie o przestarzałej metodzie.
finish()metodaZamyka bieżący ekran i wraca do poprzedniego.
Color.parseColor("#RRGGBB")IntZamienia zapis koloru na liczbę dla setTextColor.
CZĘSTE BŁĘDY

Zanim utkniesz

ZapisProblem
klasa bez : SerializableBłąd kompilacji przy putExtra — None of the following candidates is applicable.
pole typu Bitmap, View lub innej klasy bez SerializableKompiluje się, ale aplikacja zamyka się przy startActivity — w opisie błędu znajdziesz NotSerializableException i nazwę klasy, której nie da się spakować.
"ksiazka" przy wysyłaniu, "Ksiazka" przy odbiorzeOdbiór daje null. Klucze muszą być identyczne.
… as Ksiazka (bez ?)Gdy obiektu brak — awaria NullPointerException: null cannot be cast to non-null type.
brak wpisu <activity> w manifeścieActivityNotFoundException po kliknięciu. Dodawaj aktywności przez New → Activity.
@Parcelize bez wtyczki w GradleUnresolved reference: parcelize. Dopisz id("kotlin-parcelize") i zsynchronizuj.
zmiana pola obiektu na drugim ekranieLista na pierwszym ekranie się nie zmienia — drugi ekran ma kopię. Wynik odsyła się osobno (lekcja 33).
przesyłanie długiej listy ze zdjęciamiTransactionTooLargeException. Wysyłaj małe obiekty.
ZADANIA

Zadania

Zadania 1–3 rozbudowują przykład z lekcji. Zadania 4–6 wykonaj w nowych projektach Empty Views Activity. Wskazówkę otwórz dopiero wtedy, gdy utkniesz.

ZAD 1Liczba stron i gatunek★☆☆

Dodaj do klasy Ksiazka pola strony: Int i gatunek: String. Uzupełnij dane wszystkich książek i pokaż nowe pola na ekranie szczegółów. Sprawdź, że w MainActivity nie musiałeś zmieniać ani jednej linii poza listą książek.

pokaż wskazówkę
  • Dodaj dwa TextView do activity_szczegoly.xml, dwa pola w SzczegolyActivity i dwie linie w initUI().
ZAD 2Przycisk Wypożycz★☆☆

Na ekranie szczegółów dodaj przycisk Wypożycz. Jest aktywny tylko dla dostępnych książek. Po kliknięciu pokazuje Toast „Wypożyczono: Quo vadis. Zwrot za 14 dni.” i zamyka ekran.

pokaż wskazówkę
  • Aktywność przycisku ustawia btnWypozycz.isEnabled = ksiazka.dostepna.
ZAD 3Z Serializable na Parcelable★★☆

Przerób przykład tak, aby Ksiazka była Parcelable z adnotacją @Parcelize. Aplikacja ma działać tak samo. Zapisz w komentarzu na początku pliku Ksiazka.kt, które trzy miejsca w projekcie musiałeś zmienić.

pokaż wskazówkę
  • Gradle (wtyczka), klasa (adnotacja i interfejs), odbiór w SzczegolyActivity. MainActivity zostaje bez zmian.
ZAD 4Dziennik — karta ucznia★★☆

Klasa Uczen(imie, nazwisko, klasa, oceny: List<Int>). Pierwszy ekran: lista pięciu uczniów w formacie „Nowak Anna (3TP)”. Ekran szczegółów pokazuje imię i nazwisko, klasę, wszystkie oceny w jednej linii („5, 4, 6, 3”) i średnią z dwoma miejscami po przecinku. Średnia poniżej 2,0 jest czerwona, od 4,75 — zielona z dopiskiem „pasek!”.

pokaż wskazówkę
  • Lista liczb przechodzi przez Serializable bez dodatkowej pracy. Oceny w linii: uczen.oceny.joinToString(", "), średnia: uczen.oceny.average().
ZAD 5Porównywarka telefonów★★☆

Klasa Telefon(model, cena, pamiecGB, bateriaMah) i lista sześciu telefonów. Użytkownik klika dwa telefony; nad listą widać „Wybrano: 1/2”, potem „2/2”. Po drugim kliknięciu otwiera się ekran porównania, który dostaje dwa obiekty pod kluczami "pierwszy" i "drugi" i pokazuje je w dwóch kolumnach. Lepsza wartość w każdym wierszu (niższa cena, więcej pamięci, większa bateria) jest pogrubiona.

pokaż wskazówkę
  • Pierwszy wybór zapamiętaj w polu private var pierwszy: Telefon? = null. Po otwarciu ekranu porównania wyzeruj je, żeby można było porównywać od nowa. Pogrubienie: tv.setTypeface(null, Typeface.BOLD).
ZAD 6Książka kucharska★★★

Klasa Przepis(nazwa, minuty, trudnosc, skladniki: List<String>, kroki: List<String>), wersja Parcelable. Pierwszy ekran: lista przepisów („Naleśniki · 30 min”) i przełącznik Switch „Tylko do 20 minut”, który filtruje listę. Ekran przepisu: nazwa, czas, trudność w postaci gwiazdek (RatingBar tylko do odczytu), lista składników w ListView z wbudowanym wierszem simple_list_item_multiple_choice (odhaczanie kupionych) i przycisk Pokaż kroki, który otwiera trzeci ekran z krokami — ten ekran dostaje tylko listę kroków i nazwę przepisu, nie cały obiekt.

pokaż wskazówkę
  • Listę napisów prześlesz bez własnej klasy: putStringArrayListExtra("kroki", ArrayList(przepis.kroki)), odbiór: getStringArrayListExtra("kroki"). Lista wielokrotnego wyboru wymaga w XML android:choiceMode="multipleChoice".
PODSUMOWANIE

Co trzeba zapamiętać

  • Zamiast kilku wartości osobno wysyłaj jeden obiekt: putExtra("klucz", obiekt).
  • Klasa musi się na to zgodzić: : Serializable (najprościej) albo @Parcelize … : Parcelable (szybciej).
  • Odbiór: getSerializableExtra("klucz") as? Klasa lub getParcelableExtra<Klasa>("klucz") — zawsze sprawdź null.
  • Klucz przy wysyłaniu i odbiorze musi być identyczny.
  • Drugi ekran dostaje kopię obiektu — zmiany trzeba odesłać (lekcja 33).

Dokumentacja: Intent, Generator Parcelable (@Parcelize), Parcelables i Bundle — limity rozmiaru.

Postęp zapisuje się w tej przeglądarce.