Lezione 1: Funzioni componibili
Jetpack Compose si basa su funzioni componibili. Queste funzioni ti consentono di definire
la UI dell'app in modo programmatico, descrivendone l'aspetto e fornendo le dipendenze dei dati.
invece che concentrarsi sul processo di creazione dell'interfaccia utente (inizializzazione di un elemento,
allegandolo a un genitore e così via). Per creare una funzione componibile, è sufficiente aggiungere il metodo
@Composable
al nome della funzione.
Aggiungere un elemento di testo
Per iniziare, scarica la versione più recente di Android Studio e creare un'app selezionando Nuovo progetto e sotto Categoria Smartphone e tablet, seleziona Attività vuota. Assegna all'app il nome ComposeTutorial e fai clic su Fine. Il valore predefinito contiene già alcuni elementi Compose, ma in questo tutorial imparerai a per passo.
Per prima cosa, visualizza il messaggio "Hello world!". di testo aggiungendo un elemento di testo all'interno
onCreate
. Per farlo devi definire un contenuto
bloccare e chiamare il
Funzione componibile Text
. La
Il blocco setContent
definisce il layout dell'attività in cui
funzioni componibili. Le funzioni componibili possono essere chiamate solo da altre
funzioni.
Jetpack Compose usa un plug-in del compilatore Kotlin per trasformare queste funzioni componibili nel
elementi UI dell'app. Ad esempio, il componibile Text
definita dalla libreria della UI di composizione mostra un'etichetta di testo sullo schermo.
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!") } } }
Definire una funzione componibile
Per rendere componibile una funzione, aggiungi l'annotazione @Composable
.
Per fare una prova, definisci una funzione MessageCard
che sia
passa un nome e lo utilizza per configurare l'elemento di testo.
// ... 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!") }
Visualizza l'anteprima della funzione in Android Studio
L'annotazione @Preview
ti consente di visualizzare l'anteprima delle funzioni componibili in Android
Studio senza dover creare e installare l'app su un emulatore o un dispositivo Android. La
l'annotazione deve essere utilizzata su una funzione componibile che non accetta parametri. Per questo
motivo, non puoi visualizzare l'anteprima della funzione MessageCard
strato Add. Crea invece una seconda funzione denominata
PreviewMessageCard
, che chiama
MessageCard
con un parametro appropriato. Aggiungi il parametro
Annotazione @Preview
prima del giorno
@Composable
.
// ... import androidx.compose.ui.tooling.preview.Preview @Composable fun MessageCard(name: String) { Text(text = "Hello $name!") } @Preview @Composable fun PreviewMessageCard() { MessageCard("Android") }
Ricrea il progetto. L'app in sé non cambia, dato che il nuovo
La funzione PreviewMessageCard
non viene chiamata da nessuna parte,
ma Android Studio aggiunge una finestra di anteprima che puoi espandere
facendo clic sull'icona
(design/codice). Questa finestra mostra un'anteprima degli elementi UI creati dagli elementi componibili
funzioni contrassegnate con l'annotazione @Preview
. Per aggiornare
delle anteprime in qualsiasi momento, fai clic sul pulsante Aggiorna nella parte superiore della finestra di anteprima.
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!") } } }
// ... 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!") }
// ... import androidx.compose.ui.tooling.preview.Preview @Composable fun MessageCard(name: String) { Text(text = "Hello $name!") } @Preview @Composable fun PreviewMessageCard() { MessageCard("Android") }
Lezione 2: Layout
Gli elementi UI sono gerarchici, con elementi contenuti in altri elementi. In Compose, Creare una gerarchia dell'interfaccia utente richiamando funzioni componibili da altre funzioni componibili.
Aggiungi più testi
Finora hai creato la tua prima funzione componibile e l'anteprima. Per scoprire di più su Jetpack Compose di messaggistica, creerai una semplice schermata di messaggistica che contiene un elenco di messaggi può essere espanso con alcune animazioni.
Per iniziare, rendi il messaggio componibile più completo, visualizzando il nome dell'autore e una
contenuto del messaggio. Devi prima modificare il parametro componibile per accettare un
Message
invece di un oggetto
String
e aggiungine un altro
Text
componibile all'interno
MessageCard
componibile. Assicurati di aggiornare l'anteprima
.
// ... 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!") ) }
Questo codice crea due elementi di testo all'interno della visualizzazione dei contenuti. Tuttavia, poiché non hai fornito informazioni su come disporli, gli elementi di testo vengono disegnati uno sopra l'altro, rendendo illeggibile il testo.
Utilizzo di una colonna
// ... import androidx.compose.foundation.layout.Column @Composable fun MessageCard(msg: Message) { Column { Text(text = msg.author) Text(text = msg.body) } }
Aggiungere un elemento all'immagine
Arricchisci la tua scheda del messaggio aggiungendo un'immagine del profilo del mittente. Utilizza la
Gestione delle risorse
per importare un'immagine dalla tua raccolta fotografica o usa questa. Aggiungi un
Row
componibile per avere un design ben strutturato e una
Image
componibile al suo interno.
// ... 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) } } }
Configurare il layout
Il layout del messaggio ha la struttura corretta, ma gli elementi non sono ben distanziati e l'immagine è troppo grande! Per decorare o configurare un componibile, Compose utilizza dei modificatori. Loro permettono di modificare le dimensioni, il layout e l'aspetto del componibile oppure di aggiungere interazioni di alto livello come rendere cliccabile un elemento. Puoi collegarli per creare elementi componibili più sofisticati. Utilizzerai alcune per migliorare il layout.
// ... 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) } } }
// ... 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!") ) }
// ... import androidx.compose.foundation.layout.Column @Composable fun MessageCard(msg: Message) { Column { Text(text = msg.author) Text(text = msg.body) } }
// ... 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) } } }
// ... 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) } } }
Lezione 3: Material Design
Compose è progettato per supportare i principi di Material Design. Molti degli elementi dell'interfaccia utente implementano Material Design pronto all'uso. In questa lezione, imparerai a creare la tua app con Material Design widget.
Utilizzare Material Design
Il design del tuo messaggio ora ha un layout, ma ancora non sembra essere ideale.
Jetpack Compose fornisce un'implementazione di Material Design 3 e dei suoi elementi UI fuori
. Migliorerai l'aspetto del nostro MessageCard
componibili con lo stile Material Design.
Per iniziare, aggrega la funzione MessageCard
con la
Tema Material creato nel tuo progetto, ComposeTutorialTheme
,
e un Surface
.
Esegui l'operazione sia in @Preview
che in
Funzione setContent
. In questo modo i tuoi componibili
per ereditare gli stili definiti nel tema dell'app, garantendo la coerenza in tutta l'app.
Il Material Design si basa su tre pilastri: Color
,
Typography
e Shape
.
Li aggiungerai uno alla volta.
Nota: il modello Attività di scrittura vuota genera un tema predefinito per il progetto che
ti permette di personalizzare
MaterialTheme
Se hai assegnato al progetto un nome diverso da
ComposeTutorial, puoi trovare il tuo tema personalizzato nella
Theme.kt
file nel
ui.theme
sottopacchetto.
// ... 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!") ) } } }
Colore
Usa MaterialTheme.colorScheme
per applicare stili con i colori del
tema aggregato. Puoi utilizzare questi valori del tema ovunque sia necessario un colore. In questo esempio vengono utilizzati colori dinamici per la tematizzazione (definiti dalle preferenze del dispositivo).
Puoi impostare dynamicColor
su false
nel file MaterialTheme.kt
per modificare questa impostazione.
Definisci lo stile del titolo e aggiungi un bordo all'immagine.
// ... 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) } } }
Tipografia
Gli stili di Material Tipografia sono disponibili in MaterialTheme
,
devi solo aggiungerli ai componibili 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 ) } } }
Forma
Con Shape
puoi aggiungere gli ultimi ritocchi. Per prima cosa, aggrega
testo del corpo del messaggio intorno a
Surface
componibile. In questo modo, puoi personalizzare
la forma e l'elevazione del corpo del messaggio. Al messaggio viene aggiunta anche una spaziatura interna per migliorare il layout.
// ... 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 ) } } } }
Attiva tema scuro
Tema scuro (o la modalità notturna) può essere attivata per evitare un display luminoso, soprattutto di notte, o semplicemente per salvare la batteria del dispositivo. Grazie al supporto di Material Design, Jetpack Compose è in grado di affrontare il buio per impostazione predefinita. Utilizzando colori, testo e sfondi di Material Design, si adattano allo sfondo scuro.
Puoi creare più anteprime nel file come funzioni separate o aggiungere più per la stessa funzione.
Aggiungi una nuova annotazione di anteprima e attiva la modalità notturna.
// ... 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!") ) } } }
Le scelte di colore per i temi chiaro e scuro vengono definite nel file generato dall'IDE
Theme.kt
.
Finora, hai creato un elemento dell'interfaccia utente per i messaggi che mostra un'immagine e due testi con diversi stili e ha un ottimo aspetto sia con i temi chiari che con i temi scuri.
// ... 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!") ) } } }
// ... 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!") ) } } }
// ... 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) } } }
// ... @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 ) } } }
// ... 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 ) } } } }
// ... 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!") ) } } }
Lezione 4: Elenchi e animazioni
Elenchi e animazioni sono ovunque nelle app. In questa lezione scoprirai come Compose semplifica la creazione di elenchi e l'aggiunta di animazioni in modo divertente.
Creare un elenco di messaggi
Chattare con un solo messaggio è un po' solitario, quindi cambieremo la conversazione in modo che
un solo messaggio. Dovrai creare una funzione Conversation
per visualizzare più messaggi. Per questo caso d'uso, utilizza il comando
LazyColumn
e
LazyRow
. Questi elementi componibili mostrano solo gli elementi
visibili sullo schermo, quindi sono progettati per essere molto efficienti per lunghi elenchi.
In questo snippet di codice, puoi vedere che LazyColumn
ha un
items
figlio. Richiede un
List
come parametro e la rispettiva funzione lambda
riceve un parametro denominato message
(potremmo
gli abbiamo dato il nome che vogliamo) che è un'istanza di Message
.
In breve, questo lambda viene chiamato per ogni elemento della
List
. Copia il
set di dati di esempio
nel tuo progetto per aiutare il bootstrap della conversazione rapidamente.
// ... 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) } }
Animazione dei messaggi durante l'espansione
La conversazione sta diventando più interessante. È il momento di sperimentare con le animazioni. Aggiungerai
possibilità di espandere un messaggio per visualizzarne uno più lungo, animando sia le dimensioni dei contenuti che
colore di sfondo. Per archiviare questo stato dell'interfaccia utente locale, devi monitorare se un messaggio è
o meno. Per tenere traccia di questo cambiamento di stato, devi usare le funzioni
remember
e
mutableStateOf
.
Le funzioni componibili possono archiviare lo stato locale in memoria utilizzando
remember
e tieni traccia delle modifiche al valore trasmesso a
mutableStateOf
. I componenti componibili (e i relativi figli) che utilizzano
questo stato verrà ridisegnato automaticamente quando il valore viene aggiornato. Questo processo è chiamato
ricomposizione.
Utilizzando le API di stato di Compose, come remember
e
mutableStateOf
, qualsiasi modifica allo stato aggiorna automaticamente la UI.
Nota: per utilizzare correttamente il database di Kotlin, devi aggiungere le seguenti importazioni
sintassi delle proprietà con delega (la parola chiave by
). Alt+Invio o Opzione+Invio le aggiungono
per te.
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 ) } } } }
Ora puoi modificare lo sfondo dei contenuti dei messaggi in base a:
isExpanded
quando facciamo clic su un messaggio. Utilizzerai il
Modificatore clickable
per gestire gli eventi di clic nella
componibili. Invece di cambiare semplicemente il colore di sfondo
Surface
, animerai il colore di sfondo di
modificando gradualmente il suo valore
MaterialTheme.colorScheme.surface
per
MaterialTheme.colorScheme.primary
e viceversa. Per farlo,
userai la funzione animateColorAsState
. Infine,
userà il tasto di modifica animateContentSize
per animare
dimensioni del contenitore dei messaggi:
// ... 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 ) } } } }
// ... 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) } }
// ... 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 ) } } } }
// ... 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 ) } } } }
Passaggi successivi
Congratulazioni, hai terminato il tutorial su Scrivi. Hai creato una schermata della chat semplice che mostra in modo efficiente un elenco di messaggi espandibili e animati contenenti un'immagine e del testo, e il tutto in meno di 100 righe di codice.
Ecco cosa hai imparato finora:
- Definizione delle funzioni componibili
- Aggiunta di elementi diversi alla composizione
- Strutturare il componente dell'interfaccia utente utilizzando i layout componibili
- Estensione dei componibili mediante modificatori
- Creazione di un elenco efficiente
- Monitorare lo stato e modificarlo
- Aggiunta dell'interazione utente a una creatività componibile
- Animazione dei messaggi durante l'espansione
Se vuoi approfondire alcuni di questi passaggi, esplora le risorse di seguito.