Samouczek

Samouczek w Jetpack Compose

Jetpack Compose to nowoczesny pakiet narzędzi do tworzenia natywnego interfejsu Androida. Jetpack Compose upraszcza i przyspiesza tworzenie UI na Androidzie przy użyciu mniejszej ilości kodu, zaawansowanych narzędzi i intuicyjnych interfejsów API Kotlin.

W tym samouczku utworzysz prosty komponent UI z funkcjami deklaratywnymi. Nie będziesz edytować żadnych układów XML ani korzystać z Edytora układów. Zamiast tego będziesz wywoływać funkcje kompozycyjne, które definiują potrzebne elementy, a kompilator Compose zajmie się resztą.

Pełny podgląd
Pełny podgląd

Lekcja 1. Funkcje kompozycyjne

Jetpack Compose opiera się na funkcjach kompozycyjnych. Te funkcje umożliwiają automatyczne zdefiniowanie interfejsu aplikacji przez opisanie, jak powinien wyglądać i podanie zależności danych. Dzięki temu nie musisz skupiać się na procesie tworzenia interfejsu (inicjowaniu elementu, dołączaniu go do elementu nadrzędnego itd.). Aby utworzyć funkcję kompozycyjną, dodaj do jej nazwy adnotację @Composable.

Dodaj element tekstowy

Aby rozpocząć, pobierz najnowszą wersję Android Studio i utwórz aplikację, wybierając Nowy projekt, a następnie w kategorii Telefon i tablet wybierz Pusta aktywność. Nazwij swoją aplikację Composetutorial i kliknij Finish (Zakończ). Domyślny szablon zawiera już elementy tworzenia wiadomości, ale w tym samouczku utworzysz go krok po kroku.

Najpierw wyświetl tekst „Hello world!”, dodając element tekstowy w metodzie onCreate. W tym celu musisz zdefiniować blok treści i wywołać funkcję kompozycyjną Text. Blok setContent określa układ aktywności, w którym wywoływane są funkcje kompozycyjne. Funkcje kompozycyjne można wywoływać tylko z innych funkcji kompozycyjnych.

Jetpack Compose używa wtyczki kompilatora Kotlin do przekształcenia tych funkcji kompozycyjnych w elementy interfejsu aplikacji. Na przykład funkcja kompozycyjna Text, która jest zdefiniowana przez bibliotekę interfejsu tworzenia, wyświetla etykietę tekstową na ekranie.

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Text

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Text("Hello world!")
        }
    }
}
  
pokaż podgląd
ukryj podgląd

Zdefiniuj funkcję kompozycyjną

Aby funkcja była kompozycyjna, dodaj adnotację @Composable. Aby wypróbować tę funkcję, zdefiniuj funkcję MessageCard, która będzie przekazywać nazwę, i użyj jej do konfigurowania elementu tekstowego.

// ...
import androidx.compose.runtime.Composable

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MessageCard("Android")
        }
    }
}

@Composable
fun MessageCard(name: String) {
    Text(text = "Hello $name!")
}

  
pokaż podgląd
ukryj podgląd

Wyświetl podgląd funkcji w Android Studio

Adnotacja @Preview umożliwia wyświetlenie podglądu funkcji kompozycyjnych w Android Studio bez konieczności kompilowania i instalowania aplikacji na urządzeniu z Androidem lub emulatorze. Adnotacji należy używać w funkcji kompozycyjnej, która nie przyjmuje parametrów. Z tego powodu nie możesz bezpośrednio wyświetlić podglądu funkcji MessageCard. Zamiast tego utwórz drugą funkcję o nazwie PreviewMessageCard, która wywołuje funkcję MessageCard z odpowiednim parametrem. Dodaj adnotację @Preview przed obiektem @Composable.

// ...
import androidx.compose.ui.tooling.preview.Preview

@Composable
fun MessageCard(name: String) {
    Text(text = "Hello $name!")
}

@Preview
@Composable
fun PreviewMessageCard() {
    MessageCard("Android")
}
  
pokaż podgląd
ukryj podgląd

Przebuduj projekt. Aplikacja nie ulega zmianie, ponieważ nowa funkcja PreviewMessageCard nie jest nigdzie wywoływana, ale Android Studio dodaje okno podglądu, które można rozwinąć, klikając widok podzielony (projekt/kod). To okno wyświetla podgląd elementów interfejsu utworzonych przez funkcje kompozycyjne oznaczone adnotacją @Preview. Podgląd możesz zaktualizować w dowolnym momencie, klikając przycisk odświeżania u góry okna podglądu.

Podgląd funkcji kompozycyjnej w Android Studio
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Text

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Text("Hello world!")
        }
    }
}
  
pokaż podgląd
ukryj podgląd
// ...
import androidx.compose.runtime.Composable

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MessageCard("Android")
        }
    }
}

@Composable
fun MessageCard(name: String) {
    Text(text = "Hello $name!")
}

  
pokaż podgląd
ukryj podgląd
// ...
import androidx.compose.ui.tooling.preview.Preview

@Composable
fun MessageCard(name: String) {
    Text(text = "Hello $name!")
}

@Preview
@Composable
fun PreviewMessageCard() {
    MessageCard("Android")
}
  
pokaż podgląd
ukryj podgląd
Podgląd funkcji kompozycyjnej w Android Studio

Lekcja 2. Układy

Elementy interfejsu są hierarchiczne – zawierają też inne elementy. W usłudze Compose tworzysz hierarchię interfejsu, wywołując funkcje kompozycyjne z innych funkcji kompozycyjnych.

Dodawanie wielu tekstów

Masz już pierwszą funkcję kompozycyjną i podgląd. Aby odkryć więcej możliwości Jetpack Compose, musisz utworzyć prosty ekran przesyłania wiadomości zawierający listę wiadomości, które można rozwinąć za pomocą niektórych animacji.

Zacznij od wzbogacenia wiadomości kompozycyjnej przez wyświetlenie nazwy jej autora oraz treści wiadomości. Musisz najpierw zmienić parametr kompozycyjny, by akceptował obiekt Message zamiast String, i dodać kolejny element kompozycyjny Text w funkcji kompozycyjnej MessageCard. Pamiętaj, aby zaktualizować też podgląd.

// ...

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MessageCard(Message("Android", "Jetpack Compose"))
        }
    }
}

data class Message(val author: String, val body: String)

@Composable
fun MessageCard(msg: Message) {
    Text(text = msg.author)
    Text(text = msg.body)
}

@Preview
@Composable
fun PreviewMessageCard() {
    MessageCard(
        msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!")
    )
}

  
pokaż podgląd
ukryj podgląd

Ten kod tworzy w widoku treści dwa elementy tekstowe. Ponieważ jednak nie podano żadnych informacji o tym, jak je rozmieścić, elementy tekstowe są rysowane jeden na drugim, przez co tekst jest nieczytelny.

Korzystanie z kolumny

Funkcja Column pozwala rozmieścić elementy w pionie. Dodaj Column do funkcji MessageCard.
Za pomocą właściwości Row możesz rozmieścić elementy w poziomie, a za pomocą Box możesz układać je w stos.

// ...
import androidx.compose.foundation.layout.Column

@Composable
fun MessageCard(msg: Message) {
    Column {
        Text(text = msg.author)
        Text(text = msg.body)
    }
}

pokaż podgląd
ukryj podgląd

Dodaj element graficzny

Uzupełnij kartę wiadomości, dodając zdjęcie profilowe nadawcy. Użyj Menedżera zasobów, aby zaimportować obraz z biblioteki zdjęć, lub tego. Dodaj funkcję Row kompozycyjną, aby miała prawidłową strukturę, a w niej element kompozycyjny Image.

// ...
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Row
import androidx.compose.ui.res.painterResource

@Composable
fun MessageCard(msg: Message) {
    Row {
        Image(
            painter = painterResource(R.drawable.profile_picture),
            contentDescription = "Contact profile picture",
        )
    
       Column {
            Text(text = msg.author)
            Text(text = msg.body)
        }
  
    }
  
}
  
pokaż podgląd
ukryj podgląd

Konfigurowanie układu

Układ wiadomości ma odpowiednią strukturę, ale jego elementy nie są odpowiednio rozmieszczone, a obraz jest za duży. Do udekorowania lub skonfigurowania funkcji kompozycyjnej w widoku Compose używane są modyfikatory. Pozwalają one zmieniać rozmiar, układ i wygląd elementu kompozycyjnego oraz dodawać interakcje ogólne, takie jak elementy, które można kliknąć. Możesz łączyć je w łańcuchy, aby tworzyć bogatsze elementy kompozycyjne. Użyjesz ich do ulepszenia układu.

// ...
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp

@Composable
fun MessageCard(msg: Message) {
    // Add padding around our message
    Row(modifier = Modifier.padding(all = 8.dp)) {
        Image(
            painter = painterResource(R.drawable.profile_picture),
            contentDescription = "Contact profile picture",
            modifier = Modifier
                // Set image size to 40 dp
                .size(40.dp)
                // Clip image to be shaped as a circle
                .clip(CircleShape)
        )

        // Add a horizontal space between the image and the column
        Spacer(modifier = Modifier.width(8.dp))

        Column {
            Text(text = msg.author)
            // Add a vertical space between the author and message texts
            Spacer(modifier = Modifier.height(4.dp))
            Text(text = msg.body)
        }
    }
}
  
pokaż podgląd
ukryj podgląd
// ...

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MessageCard(Message("Android", "Jetpack Compose"))
        }
    }
}

data class Message(val author: String, val body: String)

@Composable
fun MessageCard(msg: Message) {
    Text(text = msg.author)
    Text(text = msg.body)
}

@Preview
@Composable
fun PreviewMessageCard() {
    MessageCard(
        msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!")
    )
}

  
pokaż podgląd
ukryj podgląd
Podgląd 2 nakładających się elementów kompozycyjnych w tekście
// ...
import androidx.compose.foundation.layout.Column

@Composable
fun MessageCard(msg: Message) {
    Column {
        Text(text = msg.author)
        Text(text = msg.body)
    }
}

pokaż podgląd
ukryj podgląd
// ...
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Row
import androidx.compose.ui.res.painterResource

@Composable
fun MessageCard(msg: Message) {
    Row {
        Image(
            painter = painterResource(R.drawable.profile_picture),
            contentDescription = "Contact profile picture",
        )
    
       Column {
            Text(text = msg.author)
            Text(text = msg.body)
        }
  
    }
  
}
  
pokaż podgląd
ukryj podgląd
// ...
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp

@Composable
fun MessageCard(msg: Message) {
    // Add padding around our message
    Row(modifier = Modifier.padding(all = 8.dp)) {
        Image(
            painter = painterResource(R.drawable.profile_picture),
            contentDescription = "Contact profile picture",
            modifier = Modifier
                // Set image size to 40 dp
                .size(40.dp)
                // Clip image to be shaped as a circle
                .clip(CircleShape)
        )

        // Add a horizontal space between the image and the column
        Spacer(modifier = Modifier.width(8.dp))

        Column {
            Text(text = msg.author)
            // Add a vertical space between the author and message texts
            Spacer(modifier = Modifier.height(4.dp))
            Text(text = msg.body)
        }
    }
}
  
pokaż podgląd
ukryj podgląd

Lekcja 3. Material Design

Funkcja tworzenia wiadomości jest zgodna z zasadami Material Design. Wiele elementów interfejsu od razu korzysta z interfejsu Material Design. Podczas tej lekcji dostosujesz styl swojej aplikacji za pomocą widżetów Material Design.

Użyj stylu Material Design

Twój projekt wiadomości ma teraz układ, ale jeszcze nie wygląda najlepiej.

Jetpack Compose udostępnia od razu implementację Material Design 3 i jego elementów interfejsu. Poprawisz wygląd naszego elementu kompozycyjnego MessageCard za pomocą stylu Material Design.

Zacznij od opakowania funkcji MessageCard motywem Material Design utworzonym w projekcie ComposeTutorialTheme oraz z Surface. Zrób to zarówno w funkcji @Preview, jak i w funkcji setContent. Umożliwi to elementom kompozycyjnym dziedziczenie stylów zdefiniowanych w motywie aplikacji, co zapewni spójność w całej aplikacji.

Material Design opiera się na 3 filarach: Color, Typography i Shape. Będziesz dodawać je pojedynczo.

Uwaga: szablon działania pustego tworzenia wiadomości generuje domyślny motyw dla projektu, który pozwala dostosować MaterialTheme. Jeśli projekt ma inną nazwę niż Composetutorial, motyw niestandardowy znajdziesz w pliku Theme.kt w podpakiecie ui.theme.

// ...

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeTutorialTheme {
                Surface(modifier = Modifier.fillMaxSize()) {
                    MessageCard(Message("Android", "Jetpack Compose"))
                }
            }
        }
    }
}

@Preview
@Composable
fun PreviewMessageCard() {
    ComposeTutorialTheme {
        Surface {
            MessageCard(
                msg = Message("Lexi", "Take a look at Jetpack Compose, it's great!")
            )
        }
    }
}


  
pokaż podgląd
ukryj podgląd

Kolor

Użyj elementu MaterialTheme.colorScheme, aby dostosować styl za pomocą kolorów z opakowanego motywu. Możesz użyć tych wartości z motywu wszędzie tam, gdzie potrzebujesz koloru. W tym przykładzie zastosowano dynamiczne kolory motywów (określone przez ustawienia urządzenia). Aby to zmienić, możesz ustawić dynamicColor na false w pliku MaterialTheme.kt.

Zmień styl tytułu i dodaj obramowanie obrazu.

// ...
import androidx.compose.foundation.border
import androidx.compose.material3.MaterialTheme

@Composable
fun MessageCard(msg: Message) {
   Row(modifier = Modifier.padding(all = 8.dp)) {
       Image(
           painter = painterResource(R.drawable.profile_picture),
           contentDescription = null,
           modifier = Modifier
               .size(40.dp)
               .clip(CircleShape)
               .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape)
       )

       Spacer(modifier = Modifier.width(8.dp))

       Column {
           Text(
               text = msg.author,
               color = MaterialTheme.colorScheme.secondary
           )

           Spacer(modifier = Modifier.height(4.dp))
           Text(text = msg.body)
       }
   }
}

  
pokaż podgląd
ukryj podgląd

Typografia

Style typografii materiału są dostępne tutaj: MaterialTheme. Wystarczy, że dodasz je do funkcji kompozycyjnych Text.

// ...

@Composable
fun MessageCard(msg: Message) {
   Row(modifier = Modifier.padding(all = 8.dp)) {
       Image(
           painter = painterResource(R.drawable.profile_picture),
           contentDescription = null,
           modifier = Modifier
               .size(40.dp)
               .clip(CircleShape)
               .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape)
       )
       Spacer(modifier = Modifier.width(8.dp))

       Column {
           Text(
               text = msg.author,
               color = MaterialTheme.colorScheme.secondary,
               style = MaterialTheme.typography.titleSmall
           )

           Spacer(modifier = Modifier.height(4.dp))

           Text(
               text = msg.body,
               style = MaterialTheme.typography.bodyMedium
           )
       }
   }
}

  
pokaż podgląd
ukryj podgląd

Kształt

Dzięki Shape możesz wprowadzić ostatnie poprawki. Najpierw opakuj tekst wiadomości wokół funkcji kompozycyjnej Surface. Pozwoli Ci to dostosować kształt i wysokość treści wiadomości. Do wiadomości zostaje też dodane dopełnienie, które zapewnia lepszy układ.

// ...
import androidx.compose.material3.Surface

@Composable
fun MessageCard(msg: Message) {
   Row(modifier = Modifier.padding(all = 8.dp)) {
       Image(
           painter = painterResource(R.drawable.profile_picture),
           contentDescription = null,
           modifier = Modifier
               .size(40.dp)
               .clip(CircleShape)
               .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape)
       )
       Spacer(modifier = Modifier.width(8.dp))

       Column {
           Text(
               text = msg.author,
               color = MaterialTheme.colorScheme.secondary,
               style = MaterialTheme.typography.titleSmall
           )

           Spacer(modifier = Modifier.height(4.dp))

           Surface(shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp) {
               Text(
                   text = msg.body,
                   modifier = Modifier.padding(all = 4.dp),
                   style = MaterialTheme.typography.bodyMedium
               )
           }
       }
   }
}

  
pokaż podgląd
ukryj podgląd

Włącz ciemny motyw

Możesz włączyć ciemny motyw (lub tryb nocny), aby ograniczyć jasność ekranu w nocy lub po prostu oszczędzać baterię. Dzięki obsłudze Material Design Jetpack Compose domyślnie może obsługiwać ciemny motyw. Jeśli użyjesz kolorów w stylu Material Design, tekst i tło będą automatycznie dostosowywać się do ciemnego tła.

Możesz utworzyć wiele podglądów w pliku jako osobne funkcje lub dodać wiele adnotacji do tej samej funkcji.

Dodaj nową adnotację do podglądu i włącz tryb nocny.

// ...
import android.content.res.Configuration

@Preview(name = "Light Mode")
@Preview(
    uiMode = Configuration.UI_MODE_NIGHT_YES,
    showBackground = true,
    name = "Dark Mode"
)
@Composable
fun PreviewMessageCard() {
   ComposeTutorialTheme {
    Surface {
      MessageCard(
        msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!")
      )
    }
   }
}
  
pokaż podgląd
ukryj podgląd

Wybór kolorów dla motywów jasnych i ciemnych jest określony w pliku Theme.kt wygenerowanym przez IDE.

Do tej pory udało Ci się utworzyć element interfejsu wiadomości, który zawiera obraz i 2 teksty w różnych stylach. Ten element wygląda dobrze zarówno w jasnym, jak i ciemnym motywie.

// ...
import android.content.res.Configuration

@Preview(name = "Light Mode")
@Preview(
    uiMode = Configuration.UI_MODE_NIGHT_YES,
    showBackground = true,
    name = "Dark Mode"
)
@Composable
fun PreviewMessageCard() {
   ComposeTutorialTheme {
    Surface {
      MessageCard(
        msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!")
      )
    }
   }
}
  
pokaż podgląd
ukryj podgląd
// ...

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeTutorialTheme {
                Surface(modifier = Modifier.fillMaxSize()) {
                    MessageCard(Message("Android", "Jetpack Compose"))
                }
            }
        }
    }
}

@Preview
@Composable
fun PreviewMessageCard() {
    ComposeTutorialTheme {
        Surface {
            MessageCard(
                msg = Message("Lexi", "Take a look at Jetpack Compose, it's great!")
            )
        }
    }
}


  
pokaż podgląd
ukryj podgląd
// ...
import androidx.compose.foundation.border
import androidx.compose.material3.MaterialTheme

@Composable
fun MessageCard(msg: Message) {
   Row(modifier = Modifier.padding(all = 8.dp)) {
       Image(
           painter = painterResource(R.drawable.profile_picture),
           contentDescription = null,
           modifier = Modifier
               .size(40.dp)
               .clip(CircleShape)
               .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape)
       )

       Spacer(modifier = Modifier.width(8.dp))

       Column {
           Text(
               text = msg.author,
               color = MaterialTheme.colorScheme.secondary
           )

           Spacer(modifier = Modifier.height(4.dp))
           Text(text = msg.body)
       }
   }
}

  
pokaż podgląd
ukryj podgląd
// ...

@Composable
fun MessageCard(msg: Message) {
   Row(modifier = Modifier.padding(all = 8.dp)) {
       Image(
           painter = painterResource(R.drawable.profile_picture),
           contentDescription = null,
           modifier = Modifier
               .size(40.dp)
               .clip(CircleShape)
               .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape)
       )
       Spacer(modifier = Modifier.width(8.dp))

       Column {
           Text(
               text = msg.author,
               color = MaterialTheme.colorScheme.secondary,
               style = MaterialTheme.typography.titleSmall
           )

           Spacer(modifier = Modifier.height(4.dp))

           Text(
               text = msg.body,
               style = MaterialTheme.typography.bodyMedium
           )
       }
   }
}

  
pokaż podgląd
ukryj podgląd
// ...
import androidx.compose.material3.Surface

@Composable
fun MessageCard(msg: Message) {
   Row(modifier = Modifier.padding(all = 8.dp)) {
       Image(
           painter = painterResource(R.drawable.profile_picture),
           contentDescription = null,
           modifier = Modifier
               .size(40.dp)
               .clip(CircleShape)
               .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape)
       )
       Spacer(modifier = Modifier.width(8.dp))

       Column {
           Text(
               text = msg.author,
               color = MaterialTheme.colorScheme.secondary,
               style = MaterialTheme.typography.titleSmall
           )

           Spacer(modifier = Modifier.height(4.dp))

           Surface(shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp) {
               Text(
                   text = msg.body,
                   modifier = Modifier.padding(all = 4.dp),
                   style = MaterialTheme.typography.bodyMedium
               )
           }
       }
   }
}

  
pokaż podgląd
ukryj podgląd
// ...
import android.content.res.Configuration

@Preview(name = "Light Mode")
@Preview(
    uiMode = Configuration.UI_MODE_NIGHT_YES,
    showBackground = true,
    name = "Dark Mode"
)
@Composable
fun PreviewMessageCard() {
   ComposeTutorialTheme {
    Surface {
      MessageCard(
        msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!")
      )
    }
   }
}
  
pokaż podgląd
ukryj podgląd
Podgląd przedstawiający kompozycje z jasnym i ciemnym motywem.

Lekcja 4. Listy i animacje

Listy i animacje są wszędzie w aplikacjach. Podczas tej lekcji dowiesz się, jak łatwo tworzyć listy i dodawać animacje za pomocą funkcji Utwórz.

Tworzenie listy wiadomości

Czat z 1 wiadomością czuje się trochę samotny, dlatego zmienimy rozmowę tak, żeby zawierała więcej niż 1 wiadomość. Musisz utworzyć funkcję Conversation, która będzie wyświetlać wiele wiadomości. W tym przypadku użyj poleceń LazyColumn i LazyRow w sekcji Compose. Te funkcje kompozycyjne renderują tylko elementy widoczne na ekranie, więc są bardzo skuteczne w przypadku długich list.

W tym fragmencie kodu widać, że LazyColumn ma element podrzędny items. Wykorzystuje on parametr List, a jego lambda otrzymuje parametr o nazwie message (mogliśmy nadać mu dowolną nazwę), który jest wystąpieniem funkcji Message. Krótko mówiąc, ta funkcja jest wywoływana w przypadku każdego elementu w parametrze List. Aby przyspieszyć rozruch rozmowy, skopiuj przykładowy zbiór danych do projektu.

// ...
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items

@Composable
fun Conversation(messages: List<Message>) {
    LazyColumn {
        items(messages) { message ->
            MessageCard(message)
        }
    }
}

@Preview
@Composable
fun PreviewConversation() {
    ComposeTutorialTheme {
        Conversation(SampleData.conversationSample)
    }
}

  
pokaż podgląd
ukryj podgląd

Animuj wiadomości przy rozwijaniu

Rozmowa staje się coraz bardziej interesująca. Czas na zabawę z animacjami. Dodasz możliwość rozwinięcia wiadomości, aby wyświetlić dłuższą wiadomość z animacją rozmiaru treści i koloru tła. Aby zapisać ten stan interfejsu lokalnego, musisz śledzić, czy wiadomość została rozwinięta. Aby śledzić zmianę stanu, musisz używać funkcji remember i mutableStateOf.

Funkcje kompozycyjne mogą przechowywać lokalny stan w pamięci za pomocą funkcji remember i śledzić zmiany wartości przekazywanej do mutableStateOf. Funkcje kompozycyjne (i ich elementy podrzędne) korzystające z tego stanu zostaną automatycznie ponownie wyświetlone po zaktualizowaniu wartości. Jest to tzw. rekompozycja.

Używanie interfejsów API stanu w Compose, takich jak remember i mutableStateOf, powoduje automatyczną aktualizację interfejsu.

Uwaga: aby prawidłowo korzystać ze składni właściwości delegowanej Kotlin (słowo kluczowe by), musisz dodać te importy. Alt+Enter lub Option+Enter doda je za Ciebie.
import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue

// ...
import androidx.compose.foundation.clickable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue

class MainActivity : ComponentActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContent {
           ComposeTutorialTheme {
               Conversation(SampleData.conversationSample)
           }
       }
   }
}

@Composable
fun MessageCard(msg: Message) {
    Row(modifier = Modifier.padding(all = 8.dp)) {
        Image(
            painter = painterResource(R.drawable.profile_picture),
            contentDescription = null,
            modifier = Modifier
                .size(40.dp)
                .clip(CircleShape)
                .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape)
        )
        Spacer(modifier = Modifier.width(8.dp))

        // We keep track if the message is expanded or not in this
        // variable
        var isExpanded by remember { mutableStateOf(false) }

        // We toggle the isExpanded variable when we click on this Column
        Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) {
            Text(
                text = msg.author,
                color = MaterialTheme.colorScheme.secondary,
                style = MaterialTheme.typography.titleSmall
            )

            Spacer(modifier = Modifier.height(4.dp))

            Surface(
                shape = MaterialTheme.shapes.medium,
                shadowElevation = 1.dp,
            ) {
                Text(
                    text = msg.body,
                    modifier = Modifier.padding(all = 4.dp),
                    // If the message is expanded, we display all its content
                    // otherwise we only display the first line
                    maxLines = if (isExpanded) Int.MAX_VALUE else 1,
                    style = MaterialTheme.typography.bodyMedium
                )
            }
        }
    }
}

  
pokaż podgląd
ukryj podgląd

Teraz możesz zmienić tło treści wiadomości w zależności od tego, czy klikniemy element isExpanded. Do obsługi zdarzeń kliknięcia w elemencie kompozycyjnym używasz modyfikatora clickable. Zamiast zmieniać kolor tła obiektu Surface, możesz utworzyć jego animację, stopniowo modyfikując jego wartość z MaterialTheme.colorScheme.surface na MaterialTheme.colorScheme.primary i odwrotnie. Użyjesz do tego funkcji animateColorAsState. Na koniec użyj modyfikatora animateContentSize, aby płynnie animować rozmiar kontenera wiadomości:

// ...
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.animateContentSize

@Composable
fun MessageCard(msg: Message) {
    Row(modifier = Modifier.padding(all = 8.dp)) {
        Image(
            painter = painterResource(R.drawable.profile_picture),
            contentDescription = null,
            modifier = Modifier
                .size(40.dp)
                .clip(CircleShape)
                .border(1.5.dp, MaterialTheme.colorScheme.secondary, CircleShape)
        )
        Spacer(modifier = Modifier.width(8.dp))

        // We keep track if the message is expanded or not in this
        // variable
        var isExpanded by remember { mutableStateOf(false) }
        // surfaceColor will be updated gradually from one color to the other
        val surfaceColor by animateColorAsState(
            if (isExpanded) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface,
        )

        // We toggle the isExpanded variable when we click on this Column
        Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) {
            Text(
                text = msg.author,
                color = MaterialTheme.colorScheme.secondary,
                style = MaterialTheme.typography.titleSmall
            )

            Spacer(modifier = Modifier.height(4.dp))

            Surface(
                shape = MaterialTheme.shapes.medium,
                shadowElevation = 1.dp,
                // surfaceColor color will be changing gradually from primary to surface
                color = surfaceColor,
                // animateContentSize will change the Surface size gradually
                modifier = Modifier.animateContentSize().padding(1.dp)
            ) {
                Text(
                    text = msg.body,
                    modifier = Modifier.padding(all = 4.dp),
                    // If the message is expanded, we display all its content
                    // otherwise we only display the first line
                    maxLines = if (isExpanded) Int.MAX_VALUE else 1,
                    style = MaterialTheme.typography.bodyMedium
                )
            }
        }
    }
}

  
pokaż podgląd
ukryj podgląd
// ...
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items

@Composable
fun Conversation(messages: List<Message>) {
    LazyColumn {
        items(messages) { message ->
            MessageCard(message)
        }
    }
}

@Preview
@Composable
fun PreviewConversation() {
    ComposeTutorialTheme {
        Conversation(SampleData.conversationSample)
    }
}

  
pokaż podgląd
ukryj podgląd
// ...
import androidx.compose.foundation.clickable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue

class MainActivity : ComponentActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContent {
           ComposeTutorialTheme {
               Conversation(SampleData.conversationSample)
           }
       }
   }
}

@Composable
fun MessageCard(msg: Message) {
    Row(modifier = Modifier.padding(all = 8.dp)) {
        Image(
            painter = painterResource(R.drawable.profile_picture),
            contentDescription = null,
            modifier = Modifier
                .size(40.dp)
                .clip(CircleShape)
                .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape)
        )
        Spacer(modifier = Modifier.width(8.dp))

        // We keep track if the message is expanded or not in this
        // variable
        var isExpanded by remember { mutableStateOf(false) }

        // We toggle the isExpanded variable when we click on this Column
        Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) {
            Text(
                text = msg.author,
                color = MaterialTheme.colorScheme.secondary,
                style = MaterialTheme.typography.titleSmall
            )

            Spacer(modifier = Modifier.height(4.dp))

            Surface(
                shape = MaterialTheme.shapes.medium,
                shadowElevation = 1.dp,
            ) {
                Text(
                    text = msg.body,
                    modifier = Modifier.padding(all = 4.dp),
                    // If the message is expanded, we display all its content
                    // otherwise we only display the first line
                    maxLines = if (isExpanded) Int.MAX_VALUE else 1,
                    style = MaterialTheme.typography.bodyMedium
                )
            }
        }
    }
}

  
pokaż podgląd
ukryj podgląd
// ...
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.animateContentSize

@Composable
fun MessageCard(msg: Message) {
    Row(modifier = Modifier.padding(all = 8.dp)) {
        Image(
            painter = painterResource(R.drawable.profile_picture),
            contentDescription = null,
            modifier = Modifier
                .size(40.dp)
                .clip(CircleShape)
                .border(1.5.dp, MaterialTheme.colorScheme.secondary, CircleShape)
        )
        Spacer(modifier = Modifier.width(8.dp))

        // We keep track if the message is expanded or not in this
        // variable
        var isExpanded by remember { mutableStateOf(false) }
        // surfaceColor will be updated gradually from one color to the other
        val surfaceColor by animateColorAsState(
            if (isExpanded) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface,
        )

        // We toggle the isExpanded variable when we click on this Column
        Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) {
            Text(
                text = msg.author,
                color = MaterialTheme.colorScheme.secondary,
                style = MaterialTheme.typography.titleSmall
            )

            Spacer(modifier = Modifier.height(4.dp))

            Surface(
                shape = MaterialTheme.shapes.medium,
                shadowElevation = 1.dp,
                // surfaceColor color will be changing gradually from primary to surface
                color = surfaceColor,
                // animateContentSize will change the Surface size gradually
                modifier = Modifier.animateContentSize().padding(1.dp)
            ) {
                Text(
                    text = msg.body,
                    modifier = Modifier.padding(all = 4.dp),
                    // If the message is expanded, we display all its content
                    // otherwise we only display the first line
                    maxLines = if (isExpanded) Int.MAX_VALUE else 1,
                    style = MaterialTheme.typography.bodyMedium
                )
            }
        }
    }
}

  
pokaż podgląd
ukryj podgląd

Dalsze kroki

Gratulujemy! Samouczek dotyczący tworzenia wiadomości został ukończony. Udało Ci się stworzyć prosty ekran czatu, który efektywnie wyświetla listę rozwijanych i animowanych wiadomości zawierających obrazy oraz tekst. Zaprojektowano ją zgodnie z zasadami Material Design z ciemnym motywem i podglądami – a wszystko to w mniej niż 100 wierszach kodu.

Oto czego już się dowiedziałeś(-aś):

  • Definiowanie funkcji kompozycyjnych
  • Dodawanie różnych elementów do funkcji kompozycyjnej
  • Tworzenie struktury komponentu interfejsu za pomocą funkcji kompozycyjnych układu
  • Rozszerzanie funkcji kompozycyjnych za pomocą modyfikatorów
  • Tworzenie skutecznej listy
  • Śledzenie stanu i modyfikowanie go
  • Dodawanie interakcji użytkownika do funkcji kompozycyjnej
  • Animowanie wiadomości przy ich rozwijaniu

Jeśli chcesz dowiedzieć się więcej o niektórych z poniższych etapów, zapoznaj się z materiałami poniżej.

Dalsze kroki

Konfiguracja
Samouczek tworzenia wiadomości został ukończony, możesz więc zacząć tworzyć w tej usłudze.
Ścieżka
Poznaj wyselekcjonowaną ścieżkę z programami i filmami, które pomogą Ci poznać i opanować Jetpack Compose.