Temi in Compose con Material 3

1. Introduzione

In questo codelab, imparerai a utilizzare i temi per le tue app in Jetpack Compose utilizzando Material Design 3. Imparerai inoltre i principali componenti di base delle combinazioni di colori, della tipografia e delle forme di Material Design 3, che ti consentono di creare temi personalizzati e accessibili per la tua applicazione.

Inoltre, scoprirai il supporto dei temi dinamici insieme a diversi livelli di enfasi.

Cosa imparerai a fare

In questo codelab, imparerai:

  • Aspetti chiave della tematizzazione di Material 3
  • Schemi di colori Material 3 e come generare temi per l'app
  • Come supportare temi dinamici e chiari/scuri per la tua app
  • Forme e caratteri tipografici per personalizzare la tua app
  • Componenti di Material 3 e personalizzazione per personalizzare lo stile della tua app

Cosa creerai

In questo codelab userai un tema per un'app client di posta chiamata Reply. Inizierai con un'applicazione senza stile, utilizzando il tema di base, e applicherai ciò che apprendi per impostare il tema dell'applicazione e supportare i temi scuri.

d15db3dc75a9d00f.png

Punto di partenza predefinito della nostra app con il tema di base.

Dovrai creare un tema con combinazioni di colori, elementi tipografici e forme e quindi applicarlo alla mailing list e alla pagina dei dettagli della tua app. Aggiungerai inoltre all'app il supporto dei temi dinamici. Alla fine del codelab, la tua app sarà supportata sia per i temi a colori sia per quelli dinamici.

1357cdbfaaa67721.png

Punto finale del codelab sui temi con temi di colore chiaro e dinamici.

1357cdbfaaa67721.png

Punto finale del codelab sulla tematizzazione con tema scuro e dinamico.

Che cosa ti serve

2. Preparazione

In questo passaggio scaricherai il codice completo dell'app di risposta che applicherai in questo codelab.

Ottieni il codice

Il codice di questo codelab è disponibile nel repository GitHub di android-compose-codelabs. Per clonarlo, esegui:

$ git clone https://github.com/googlecodelabs/android-compose-codelabs

In alternativa, puoi scaricare due file ZIP:

Dai un'occhiata all'app di esempio

Il codice che hai appena scaricato contiene il codice per tutti i codelab di Compose disponibili. Per completare questo codelab, apri il progetto ThemingCodelab in Android Studio.

Ti consigliamo di iniziare con il codice nel ramo principale e di seguire la procedura passo passo del codelab secondo i tuoi tempi. Puoi eseguire in qualsiasi momento una delle due versioni in Android Studio modificando il ramo Git del progetto.

Esplorazione del codice di avvio

Il codice principale contiene un pacchetto UI con i seguenti pacchetti e file principali con cui interagisci:

  • MainActivity.kt - Attività del punto di ingresso da cui avvii l'app di risposta.
  • com.example.reply.ui.theme - Questo pacchetto contiene temi, elementi tipografici e combinazioni di colori. In questo pacchetto aggiungerai i temi Material.
  • com.example.reply.ui.components: contiene i componenti personalizzati dell'app, come voci di elenco, barre delle app e così via. Applicherai i temi a questi componenti.
  • ReplyApp.kt - Questa è la nostra funzione componibile principale in cui inizierà l'albero dell'interfaccia utente. In questo file applicherai i temi di primo livello.

Questo codelab è incentrato sui file del pacchetto ui.

3. Tema Material 3

Jetpack Compose offre un'implementazione di Material Design, un sistema di progettazione completo per la creazione di interfacce digitali. I componenti di Material Design (pulsanti, schede, interruttori e così via) si basano sui temi Material, che consentono di personalizzare in modo sistematico il Material Design per rispecchiare meglio il brand del prodotto.

Il tema Material 3 comprende i seguenti sottosistemi per aggiungerli alla tua app: schema di colori, tipografia e forme. Quando personalizzi questi valori, le modifiche vengono applicate automaticamente nei componenti M3 che utilizzi per creare la tua app. Esaminiamo ogni sottosistema e lo implementiamo nell'app di esempio.

Sistemi secondari di Material Design: colore, tipografia e forme.

Sottosistema Material 3: colori, tipografia e forme.

4. Palette colori

La base di una combinazione di colori è l'insieme di cinque colori principali, ognuno dei quali è correlato a una tavolozza tonale di 13 toni utilizzati dai componenti di Material 3.

Cinque colori fondamentali di base per la creazione di una tematizzazione M3.

Cinque colori di base per la creazione di un tema M3.

Ogni colore intenso (primario, secondario e terziario) viene quindi fornito in quattro colori compatibili di diverse tonalità per l'accoppiamento, l'enfasi e l'espressione visiva.

Quattro colori tonali di colore di base primari, secondari e terziari.

Quattro colori tonali di colore di base primari, secondari e terziari.

Analogamente, anche i colori neutri sono suddivisi in quattro tonalità compatibili utilizzate per le superfici e lo sfondo. Sono importanti anche per mettere in evidenza le icone di testo quando vengono posizionate su qualsiasi superficie.

Quattro colori tonali di colori neutri di base.

Quattro colori neutri di base.

Scopri di più sui ruoli colore e combinazione di colori.

Generazione di combinazioni di colori

Anche se puoi creare manualmente una ColorScheme personalizzata, spesso è più facile generarne uno utilizzando i colori di origine del tuo brand. A questo scopo, puoi utilizzare lo strumento Generatore di temi di materiali e, se vuoi, esportare il codice della tematizzazione di Compose.

Puoi scegliere qualsiasi colore che preferisci, ma nel nostro caso d'uso utilizzerai il colore principale predefinito per le risposte #825500. Fai clic sul colore Principale nella sezione Colori principali a sinistra e aggiungi il codice nel selettore colori.

294f73fc9d2a570e.png

Aggiunta del codice del colore principale nel generatore di temi Material.

Dopo aver aggiunto il colore principale nel generatore di temi Material, dovresti vedere il seguente tema e l'opzione per l'esportazione nell'angolo in alto a destra. Per questo codelab, esporterai il tema in Jetpack Compose.

nell'angolo in alto a destra dello strumento per la creazione di temi di materiali con l'opzione per l'esportazione.

Strumento per la creazione di temi di materiali con l'opzione di esportazione nell'angolo in alto a destra.

Il colore principale #825500 genera il seguente tema che aggiungerai all'app. Material 3 offre un'ampia gamma di ruoli cromatici per esprimere in modo flessibile lo stato, l'evidenza e l'enfasi di un componente.

Combinazione di colori chiaro e scuro esportata dal colore principale.

Combinazione di colori chiari e scuri esportata dal colore principale.

The Color.kt file generato contiene i colori del tema con tutti i ruoli definiti per i colori del tema chiaro e scuro.

Color.kt

package com.example.reply.ui.theme
import androidx.compose.ui.graphics.Color

val md_theme_light_primary = Color(0xFF825500)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFFFDDB3)
val md_theme_light_onPrimaryContainer = Color(0xFF291800)
val md_theme_light_secondary = Color(0xFF6F5B40)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFFBDEBC)
val md_theme_light_onSecondaryContainer = Color(0xFF271904)
val md_theme_light_tertiary = Color(0xFF51643F)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFD4EABB)
val md_theme_light_onTertiaryContainer = Color(0xFF102004)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFFFBFF)
val md_theme_light_onBackground = Color(0xFF1F1B16)
val md_theme_light_surface = Color(0xFFFFFBFF)
val md_theme_light_onSurface = Color(0xFF1F1B16)
val md_theme_light_surfaceVariant = Color(0xFFF0E0CF)
val md_theme_light_onSurfaceVariant = Color(0xFF4F4539)
val md_theme_light_outline = Color(0xFF817567)
val md_theme_light_inverseOnSurface = Color(0xFFF9EFE7)
val md_theme_light_inverseSurface = Color(0xFF34302A)
val md_theme_light_inversePrimary = Color(0xFFFFB951)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF825500)
val md_theme_light_outlineVariant = Color(0xFFD3C4B4)
val md_theme_light_scrim = Color(0xFF000000)

val md_theme_dark_primary = Color(0xFFFFB951)
val md_theme_dark_onPrimary = Color(0xFF452B00)
val md_theme_dark_primaryContainer = Color(0xFF633F00)
val md_theme_dark_onPrimaryContainer = Color(0xFFFFDDB3)
val md_theme_dark_secondary = Color(0xFFDDC2A1)
val md_theme_dark_onSecondary = Color(0xFF3E2D16)
val md_theme_dark_secondaryContainer = Color(0xFF56442A)
val md_theme_dark_onSecondaryContainer = Color(0xFFFBDEBC)
val md_theme_dark_tertiary = Color(0xFFB8CEA1)
val md_theme_dark_onTertiary = Color(0xFF243515)
val md_theme_dark_tertiaryContainer = Color(0xFF3A4C2A)
val md_theme_dark_onTertiaryContainer = Color(0xFFD4EABB)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF1F1B16)
val md_theme_dark_onBackground = Color(0xFFEAE1D9)
val md_theme_dark_surface = Color(0xFF1F1B16)
val md_theme_dark_onSurface = Color(0xFFEAE1D9)
val md_theme_dark_surfaceVariant = Color(0xFF4F4539)
val md_theme_dark_onSurfaceVariant = Color(0xFFD3C4B4)
val md_theme_dark_outline = Color(0xFF9C8F80)
val md_theme_dark_inverseOnSurface = Color(0xFF1F1B16)
val md_theme_dark_inverseSurface = Color(0xFFEAE1D9)
val md_theme_dark_inversePrimary = Color(0xFF825500)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFFFFB951)
val md_theme_dark_outlineVariant = Color(0xFF4F4539)
val md_theme_dark_scrim = Color(0xFF000000)


val seed = Color(0xFF825500)

The Theme.kt file generato contiene una configurazione per le combinazioni di colori chiari e scuri e per il tema dell'app. Contiene anche la funzione componibile principale della tematizzazione, AppTheme().

Theme.kt

package com.example.reply.ui.theme

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable


private val LightColors = lightColorScheme(
   primary = md_theme_light_primary,
   onPrimary = md_theme_light_onPrimary,
   primaryContainer = md_theme_light_primaryContainer,
   onPrimaryContainer = md_theme_light_onPrimaryContainer,
   secondary = md_theme_light_secondary,
   onSecondary = md_theme_light_onSecondary,
   secondaryContainer = md_theme_light_secondaryContainer,
   onSecondaryContainer = md_theme_light_onSecondaryContainer,
   tertiary = md_theme_light_tertiary,
   onTertiary = md_theme_light_onTertiary,
   tertiaryContainer = md_theme_light_tertiaryContainer,
   onTertiaryContainer = md_theme_light_onTertiaryContainer,
   error = md_theme_light_error,
   errorContainer = md_theme_light_errorContainer,
   onError = md_theme_light_onError,
   onErrorContainer = md_theme_light_onErrorContainer,
   background = md_theme_light_background,
   onBackground = md_theme_light_onBackground,
   surface = md_theme_light_surface,
   onSurface = md_theme_light_onSurface,
   surfaceVariant = md_theme_light_surfaceVariant,
   onSurfaceVariant = md_theme_light_onSurfaceVariant,
   outline = md_theme_light_outline,
   inverseOnSurface = md_theme_light_inverseOnSurface,
   inverseSurface = md_theme_light_inverseSurface,
   inversePrimary = md_theme_light_inversePrimary,
   surfaceTint = md_theme_light_surfaceTint,
   outlineVariant = md_theme_light_outlineVariant,
   scrim = md_theme_light_scrim,
)


private val DarkColors = darkColorScheme(
   primary = md_theme_dark_primary,
   onPrimary = md_theme_dark_onPrimary,
   primaryContainer = md_theme_dark_primaryContainer,
   onPrimaryContainer = md_theme_dark_onPrimaryContainer,
   secondary = md_theme_dark_secondary,
   onSecondary = md_theme_dark_onSecondary,
   secondaryContainer = md_theme_dark_secondaryContainer,
   onSecondaryContainer = md_theme_dark_onSecondaryContainer,
   tertiary = md_theme_dark_tertiary,
   onTertiary = md_theme_dark_onTertiary,
   tertiaryContainer = md_theme_dark_tertiaryContainer,
   onTertiaryContainer = md_theme_dark_onTertiaryContainer,
   error = md_theme_dark_error,
   errorContainer = md_theme_dark_errorContainer,
   onError = md_theme_dark_onError,
   onErrorContainer = md_theme_dark_onErrorContainer,
   background = md_theme_dark_background,
   onBackground = md_theme_dark_onBackground,
   surface = md_theme_dark_surface,
   onSurface = md_theme_dark_onSurface,
   surfaceVariant = md_theme_dark_surfaceVariant,
   onSurfaceVariant = md_theme_dark_onSurfaceVariant,
   outline = md_theme_dark_outline,
   inverseOnSurface = md_theme_dark_inverseOnSurface,
   inverseSurface = md_theme_dark_inverseSurface,
   inversePrimary = md_theme_dark_inversePrimary,
   surfaceTint = md_theme_dark_surfaceTint,
   outlineVariant = md_theme_dark_outlineVariant,
   scrim = md_theme_dark_scrim,
)

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
   val colors = if (!useDarkTheme) {
       LightColors
   } else {
       DarkColors
   }

   MaterialTheme(
       colorScheme = colors,
       content = content
   )
}

L'elemento principale per l'implementazione dei temi in Jetpack Compose è il componibile MaterialTheme.

Aggrega il componibile MaterialTheme() nella funzione AppTheme(), che accetta due parametri:

  • useDarkTheme: questo parametro è collegato alla funzione isSystemInDarkTheme() per osservare le impostazioni dei temi di sistema e applicare il tema chiaro o scuro. Se vuoi mantenere manualmente l'app in un tema chiaro o scuro, puoi passare un valore booleano a useDarkTheme.
  • content: i contenuti a cui verrà applicato il tema.

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
   val colors = if (!useDarkTheme) {
       LightColors
   } else {
       DarkColors
   }

   MaterialTheme(
       colorScheme = colors,
       content = content
   )
}

Se provi a eseguire l'app ora, dovresti vedere che ha lo stesso aspetto. Anche se hai importato la nostra nuova combinazione di colori con nuovi colori per la tema, continuerai a vedere la tematizzazione di base perché non hai applicato il tema all'app Compose.

App con temi di base quando non è applicato alcun tema.

App con temi di base quando non è applicato alcun tema.

Per applicare il nuovo tema, in MainActivity.kt aggrega l'elemento componibile principale ReplyApp con la funzione di tematizzazione principale, AppTheme().

MainActivity.kt

setContent {
   val uiState by viewModel.uiState.collectAsStateWithLifecycle()

   AppTheme {
       ReplyApp(/*..*/)
   }
}

Aggiornerai anche le funzioni di anteprima per vedere il tema applicato alle anteprime dell'app. Aggrega il componibile ReplyApp all'interno di ReplyAppPreview() con AppTheme per applicarli alle anteprime.

Hai definito entrambi i temi di sistema chiaro e scuro nei parametri di anteprima, quindi vedrai entrambe le anteprime.

MainActivity.kt

@Preview(
   uiMode = Configuration.UI_MODE_NIGHT_YES,
   name = "DefaultPreviewDark"
)
@Preview(
   uiMode = Configuration.UI_MODE_NIGHT_NO,
   name = "DefaultPreviewLight"
)
@Composable
fun ReplyAppPreview() {
   AppTheme {
       ReplyApp(
           replyHomeUIState = ReplyHomeUIState(
               emails = LocalEmailsDataProvider.allEmails
           )
       )
   }
}

Se esegui l'app ora, dovresti vedere le anteprime dell'app con i colori del tema importati anziché il tema di base.

fddf7b9cc99b1fe3.png be7a661b4553167b.png

App con tema di base (a sinistra).

App con tema a colori importato (a destra).

674cec6cc12db6a0.png

Anteprime dell'app con tema chiaro e scuro con temi a colori importati.

Material 3 supporta combinazioni di colori chiari e scuri. Hai eseguito il wrapping dell'app solo con il tema importato. I componenti di Material 3 utilizzano ruoli per i colori predefiniti.

Scopriamo quali sono i ruoli dei colori e come utilizzarli prima di iniziare ad aggiungerli all'app.

Ruoli con colore e accessibilità

Ogni ruolo del colore può essere utilizzato in diversi modi, a seconda dello stato, dell'evidenza e dell'enfasi del componente.

1f184a05ea57aa84.png

Ruoli colore dei colori primari, secondari e terziari.

Principale è il colore di base, utilizzato per i componenti principali, come i pulsanti in evidenza e gli stati attivi.

Il colore della chiave secondaria viene utilizzato per i componenti meno visibili nell'interfaccia utente, come i chip di filtro.

Il colore chiave terziario viene utilizzato per creare contrasto, mentre per lo sfondo e le superfici dell'app vengono utilizzati colori neutri.

Il sistema di colori del materiale fornisce valori dei toni e misure standard che possono essere utilizzati per soddisfare rapporti di contrasto accessibili. Utilizza on-primary sopra il principale, on-primary-container sopra il contenitore principale e lo stesso vale per altri colori complementari e neutri per creare un contrasto accessibile per l'utente.

Per ulteriori informazioni, consulta la sezione Ruoli e accessibilità dei colori.

Elevazioni tonali e ombre

Il materiale 3 rappresenta l'elevazione principalmente con overlay di colori tonali. Si tratta di un nuovo modo per differenziare i container e le superfici l'uno dall'altro: l'aumento dell'elevazione tonale richiede un tono più evidente, oltre alle ombre.

Elevazione tonale con elevazione delle ombre Elevazione tonale al livello 2 che prende il colore dall'area del colore principale.

Anche gli overlay elevazione nei temi scuri sono stati trasformati in overlay a colori tonali in Material Design 3. Il colore dell'overlay proviene dall'area del colore principale.

La superficie M3, il componibile di supporto dietro la maggior parte dei componenti M3, include il supporto per l'elevazione dei toni e delle ombre:

Surface(
   modifier = modifier,
   tonalElevation = {..}
   shadowElevation = {..}
) {
   Column(content = content)
}

Aggiunta di colori all'app in corso...

Se esegui l'app, puoi vedere i colori esportati che vengono mostrati nell'app in cui i componenti assumono colori predefiniti. Ora che siamo a conoscenza dei ruoli dei colori e dell'utilizzo, applichiamo un tema all'app con i ruoli dei colori corretti.

be7a661b4553167b.png

App con tema cromatico e componenti che assumono ruoli colore predefiniti.

Colori superficie

Nella schermata Home, inizierai a racchiudere l'app principale componibile in un Surface() per fornire la base su cui posizionare i contenuti dell'app. Apri MainActivity.kt e aggrega il componibile ReplyApp() con Surface.

Devi fornire anche un'elevazione tonale di 5.dp per conferire alla superficie un colore tonale dell'area principale, in modo da creare un contrasto con l'elemento dell'elenco e con la barra di ricerca nella parte superiore. Per impostazione predefinita, l'elevazione dei toni e delle ombre della superficie è 0.dp.

MainActivity.kt

AppTheme {
   Surface(tonalElevation = 5.dp) {
       ReplyApp(
           replyHomeUIState = uiState,
          // other parameters
         )
   }
}

Se in questo momento l'applicazione è in esecuzione e vedi sia la pagina Elenco che quella dei dettagli, dovresti vedere la superficie tonale applicata all'intera app.

be7a661b4553167b.png e70d762495173610.png

Sfondo dell'app senza superficie e colore tonale (a sinistra).

Sfondo dell'app con superficie e colore tonale applicati (a destra).

Colori della barra delle app

La nostra barra di ricerca personalizzata in alto non ha uno sfondo chiaro per la progettazione. Per impostazione predefinita, viene ripristinata la superficie di base predefinita. Puoi fornire uno sfondo per ottenere una chiara separazione.

5779fc399d8a8187.png

Barra di ricerca personalizzata senza sfondo (a sinistra).

Barra di ricerca personalizzata con uno sfondo (a destra).

Ora modificherai ui/components/ReplyAppBars.kt, che contiene la barra delle app. Aggiungerai MaterialTheme.colorScheme.background a Modifier di Row componibile.

ReplyAppBars.kt

@Composable
fun ReplySearchBar(modifier: Modifier = Modifier) {
   Row(
       modifier = modifier
           .fillMaxWidth()
           .padding(16.dp)
           .background(MaterialTheme.colorScheme.background),
       verticalAlignment = Alignment.CenterVertically
   ) {
       // Search bar content
   }
}

Ora dovresti vedere una chiara separazione tra la superficie tonale e la barra delle app con colore di sfondo.

b1b374b801dadc06.png

Barra di ricerca con colore di sfondo sopra la superficie tonale.

Colori dei pulsanti di azione mobile

70ceac87233fe466.png

FAB grande senza alcun tema applicato (a sinistra).

FAB grande a tema con colore terziario (a destra).

Nella schermata Home, puoi migliorare l'aspetto del pulsante di azione mobile (FAB) in modo che possa distinguersi come pulsante di invito all'azione. Per implementarlo, devi applicare un colore di contrasto terziario.

Nel file ReplyListContent.kt, aggiorna il valore containerColor del FAB con il colore tertiaryContainer e il colore dei contenuti a onTertiaryContainer per mantenere l'accessibilità e il contrasto di colore.

ReplyListContent.kt

ReplyInboxScreen(/*..*/) {
// Email list content
  LargeFloatingActionButton(
    containerColor = MaterialTheme.colorScheme.tertiaryContainer,
    contentColor = MaterialTheme.colorScheme.onTertiaryContainer
  ){
   /*..*/   
  }
}

Esegui l'app per vedere il tuo tema FAB. Per questo codelab, utilizzerai LargeFloatingActionButton.

Colori della carta

La mailing list sulla schermata Home utilizza un componente della scheda. Per impostazione predefinita, si tratta di una scheda piena che utilizza il colore della variante della superficie per il colore del contenitore, in modo da garantire una chiara separazione tra il colore della superficie e quello della scheda. Compose fornisce anche implementazioni di ElevatedCard e OutlinedCard.

Puoi evidenziare ulteriormente alcuni elementi importanti fornendo tonalità di colore secondarie. Puoi modificare ui/components/ReplyEmailListItem.kt aggiornando il colore del contenitore delle schede utilizzando CardDefaults.cardColors() per le email importanti:

ReplyEmailListItem.kt

Card(
   modifier =  modifier
       .padding(horizontal = 16.dp, vertical = 4.dp)
       .semantics { selected = isSelected }
       .clickable { navigateToDetail(email.id) },
   colors = CardDefaults.cardColors(
       containerColor = if (email.isImportant)
           MaterialTheme.colorScheme.secondaryContainer
       else MaterialTheme.colorScheme.surfaceVariant
   )
){
  /*..*/   
}

5818200be0b01583.png 9367d40023db371d.png

Evidenzia l'elemento dell'elenco utilizzando il colore del contenitore secondario sulla superficie tonale.

Colore elemento elenco dettagli

Ora hai scelto un tema per la schermata Home. Dai un'occhiata alla pagina dei dettagli facendo clic su uno degli elementi della mailing list.

7a9ea7cf3e91e9c7.png 79b3874aeca4cd1.png

Pagina dei dettagli predefinita senza voce dell'elenco a tema (a sinistra).

Dettaglio dell'elemento dell'elenco con tema di sfondo applicato (a destra).

All'elemento dell'elenco non è applicato alcun colore, per cui verrà ripristinato il colore predefinito della superficie tonale. Applicherai il colore di sfondo all'elemento dell'elenco per creare una separazione e aggiungi una spaziatura interna per fornire una spaziatura attorno allo sfondo.

ReplyEmailThreadItem.kt

@Composable
fun ReplyEmailThreadItem(
   email: Email,
   modifier: Modifier = Modifier
) {
   Column(
       modifier = modifier
           .fillMaxWidth()
           .padding(16.dp)
           .background(MaterialTheme.colorScheme.background)
           .padding(20.dp)
    ) {
      // List item content
    }
}

Puoi notare che, semplicemente fornendo lo sfondo, la superficie tonale e l'elemento dell'elenco sono chiaramente separati.

Ora hai sia la home page che la pagina dei dettagli con i ruoli e l'utilizzo corretti per i colori . Vediamo in che modo la tua app può sfruttare i colori dinamici per offrire un'esperienza ancora più personalizzata e coesa.

5. Aggiunta di colori dinamici nell'app

Il colore dinamico è la parte fondamentale di Material 3, in cui un algoritmo ricava i colori personalizzati dallo sfondo di un utente per applicarli alle app e all'UI di sistema.

I temi dinamici rendono le tue app più personalizzate. Inoltre, offre agli utenti un'esperienza coerente e senza interruzioni con il tema del sistema.

Il colore dinamico è disponibile su Android 12 e versioni successive. Se è disponibile il colore dinamico, puoi impostare una combinazione di colori dinamica utilizzando dynamicDarkColorScheme() o dynamicLightColorScheme(). In caso contrario, devi tornare all'utilizzo di un valore ColorScheme chiaro o scuro predefinito.

Sostituisci il codice della funzione AppTheme nel file Theme.kt con il codice seguente:

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean =  isSystemInDarkTheme(),
   content: @Composable () -> Unit
) {
   val context = LocalContext.current
   val colors = when {
       (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) -> {
           if (useDarkTheme) dynamicDarkColorScheme(context)
           else dynamicLightColorScheme(context)
       }
       useDarkTheme -> DarkColors
       else -> LightColors
   }
   
      MaterialTheme(
       colorScheme = colors,
       content = content
     )
}

fecc63b4c6034236.png

Tema dinamico estratto dallo sfondo di Android 13.

Quando esegui ora l'app, dovresti vedere i temi dinamici applicati utilizzando lo sfondo predefinito di Android 13.

Puoi anche voler applicare uno stile dinamico alla barra di stato a seconda della combinazione di colori utilizzata per il tema dell'app.

1095e2b2c1ffdc14.png

App senza colore della barra di stato applicato (a sinistra).

App con colore della barra di stato applicato (a destra).

Per aggiornare il colore della barra di stato a seconda del colore principale del tema, aggiungi il colore della barra di stato dopo la selezione della combinazione di colori nel componibile AppTheme:

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean =  isSystemInDarkTheme(),
   content: @Composable () -> Unit
) {
 
 // color scheme selection code

 // Add primary status bar color from chosen color scheme.
 val view = LocalView.current
 if (!view.isInEditMode) {
    SideEffect {
        val window = (view.context as Activity).window
        window.statusBarColor = colors.primary.toArgb()
        WindowCompat
            .getInsetsController(window, view)
            .isAppearanceLightStatusBars = useDarkTheme
    }
 }
   
  MaterialTheme(
    colorScheme = colors,
     content = content
   )
}

Quando esegui l'app, dovresti vedere la barra di stato che assume il colore principale. Puoi anche provare i temi dinamici chiari e scuri modificando il tema scuro del sistema.

69093b5bce31fd43.png

Tema chiaro dinamico (sinistra) e tema scuro (destra) applicato con lo sfondo predefinito di Android 13.

Finora, hai applicato alla tua app colori che ne hanno migliorato l'aspetto. Tuttavia, puoi vedere che tutto il testo nell'app ha le stesse dimensioni, quindi ora puoi aggiungere elementi tipografici all'app.

6. Tipografia

Material Design 3 definisce una scala del tipo. La denominazione e il raggruppamento sono stati semplificati in: visualizzazione, titolo, titolo, corpo ed etichetta, con dimensioni grandi, medie e piccole per ciascun elemento.

999a161dcd9b0ec4.png

Scala tipo Material 3.

Definizione della tipografia

Compose fornisce la classe M3 Typography, insieme alle classi TextStyle e font-related esistenti, per modellare la scala del tipo Material 3.

Il costruttore tipografia offre valori predefiniti per ogni stile, quindi puoi omettere i parametri che non vuoi personalizzare. Per ulteriori informazioni, consulta la sezione sugli stili tipografici e sui relativi valori predefiniti.

Utilizzerai cinque stili tipografici nella tua app: headlineSmall, titleLarge, bodyLarge, bodyMedium e labelMedium. Questi stili copriranno sia la schermata Home sia la schermata dei dettagli.

Schermata che mostra l'utilizzo della tipografia per titolo, etichetta e stile del corpo.

Schermata che mostra l'utilizzo della tipografia per titolo, etichetta e stile del corpo.

Successivamente, vai al pacchetto ui/theme e apri Type.kt. Aggiungi il codice seguente per fornire la tua implementazione per alcuni stili di testo anziché per i valori predefiniti:

Type.kt

val typography = Typography(
   headlineSmall = TextStyle(
       fontWeight = FontWeight.SemiBold,
       fontSize = 24.sp,
       lineHeight = 32.sp,
       letterSpacing = 0.sp
   ),
   titleLarge = TextStyle(
       fontWeight = FontWeight.Normal,
       fontSize = 18.sp,
       lineHeight = 28.sp,
       letterSpacing = 0.sp
   ),
   bodyLarge = TextStyle(
       fontWeight = FontWeight.Normal,
       fontSize = 16.sp,
       lineHeight = 24.sp,
       letterSpacing = 0.15.sp
   ),
   bodyMedium = TextStyle(
       fontWeight = FontWeight.Medium,
       fontSize = 14.sp,
       lineHeight = 20.sp,
       letterSpacing = 0.25.sp
   ),
   labelMedium = TextStyle(
       fontWeight = FontWeight.SemiBold,
       fontSize = 12.sp,
       lineHeight = 16.sp,
       letterSpacing = 0.5.sp
   )
)

La tipografia è ora definita. Per aggiungerlo al tema, passalo al componibile MaterialTheme() all'interno di AppTheme:

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
  // dynamic theming content

   MaterialTheme(
       colorScheme = colors,
       typography = typography,
       content = content
   )
}

Lavorare con la tipografia

Proprio come con i colori, accederai allo stile tipografico per il tema corrente utilizzando MaterialTheme.typography. Questo fornisce all'istanza tipografia di utilizzare tutta la tipografia definita in Type.k.

Text(
   text = "Hello M3 theming",
   style = MaterialTheme.typography.titleLarge
)

Text(
   text = "you are learning typography",
   style = MaterialTheme.typography.bodyMedium
)

È probabile che il tuo prodotto non abbia bisogno di tutti i 15 stili predefiniti della scala per il tipo di Material Design. In questo codelab, vengono scelte cinque dimensioni, mentre le altre vengono omesse.

Poiché non hai applicato gli elementi tipografici ai componibili Text(, per impostazione predefinita tutto il testo viene ripristinato su Typography.bodyLarge.

Tipografia elenco casa

A questo punto, applica gli elementi tipografici alla funzione ReplyEmailListItem in ui/components/ReplyEmailListItem.kt per creare una distinzione tra titoli ed etichette:

ReplyEmailListItem.kt

Text(
   text = email.sender.firstName,
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = email.createdAt,
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = email.subject,
   style = MaterialTheme.typography.titleLarge,
   modifier = Modifier.padding(top = 12.dp, bottom = 8.dp),
)

Text(
   text = email.body,
   maxLines = 2,
   style = MaterialTheme.typography.bodyLarge,
   overflow = TextOverflow.Ellipsis
)

90645c0765167bb7.png 6c4af2f412c18bfb.png

Schermata Home senza elementi tipografici applicati (a sinistra).

Schermata Home con tipografia applicata (a destra).

Tipografia dell'elenco dettagliato

Analogamente, aggiungerai la grafica nella schermata dei dettagli aggiornando tutti gli elementi componibili testuali di ReplyEmailThreadItem in ui/components/ReplyEmailThreadItem.kt:

ReplyEmailThreadItem.kt

Text(
   text = email.sender.firstName,
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = stringResource(id = R.string.twenty_mins_ago),
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = email.subject,
   style = MaterialTheme.typography.bodyMedium,
   modifier = Modifier.padding(top = 12.dp, bottom = 8.dp),
)

Text(
   text = email.body,
   style = MaterialTheme.typography.bodyLarge,
   color = MaterialTheme.colorScheme.onSurfaceVariant
)

543ac09e43d8761.png 3412771e95a45f36.png

Schermata dei dettagli senza elementi tipografici applicati (a sinistra).

Schermata dei dettagli con tipografia applicata (a destra).

Personalizzazione tipografia

Con Compose, è molto facile personalizzare lo stile del testo o fornire un carattere personalizzato. Puoi modificare TextStyle per personalizzare il tipo di carattere, la famiglia di caratteri, la spaziatura tra le lettere e così via.

Lo stile di testo verrà modificato nel file theme/Type.kt e le modifiche verranno applicate a tutti i componenti che lo utilizzano.

Aggiorna fontWeight in SemiBold e lineHeight in 32.sp per titleLarge, che viene utilizzato per l'oggetto della voce dell'elenco. Metterà più enfasi sull'argomento e garantirà una chiara separazione.

Type.kt

...
titleLarge = TextStyle(
   fontWeight = FontWeight.SemiBold,
   fontSize = 18.sp,
   lineHeight = 32.sp,
   letterSpacing = 0.0.sp
),
...

f8d2212819eb0b61.png

Applicare elementi tipografici personalizzati al testo dell'oggetto.

7. Forme

Le superfici dei materiali possono essere mostrate in diverse forme. Modella l'attenzione diretta, identifica i componenti, comunica lo stato ed esprimi il brand.

Definizione delle forme

Compose fornisce alla classe Shapes parametri ampliati per implementare nuove forme M3. La scala di forma M3, simile alla scala del tipo, consente di utilizzare una gamma espressiva di forme nell'interfaccia utente.

Nella scala della forma esistono diverse dimensioni di forme:

  • Molto ridotta
  • Piccolo
  • Medie
  • Grande
  • Molto grande

Per impostazione predefinita, ogni forma ha un valore predefinito che può essere sostituito. Nella tua app, utilizzerai la forma media per modificare l'elemento dell'elenco, ma puoi dichiarare anche altre forme. Crea un nuovo file denominato Shape.kt nel pacchetto ui/theme e aggiungi il codice per le forme:

Shape.kt

package com.example.reply.ui.theme

import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp

val shapes = Shapes(
   extraSmall = RoundedCornerShape(4.dp),
   small = RoundedCornerShape(8.dp),
   medium = RoundedCornerShape(16.dp),
   large = RoundedCornerShape(24.dp),
   extraLarge = RoundedCornerShape(32.dp)
)

Ora che hai definito il tuo shapes, passalo all'M3 MaterialTheme come hai fatto per i colori e gli aspetti tipografici:

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
  // dynamic theming content

   MaterialTheme(
       colorScheme = colors,
       typography = typography,
       shapes = shapes
       content = content
   )
}

Utilizzo delle forme

Proprio come il colore e la tipografia, puoi applicare forme ai componenti Material utilizzando MaterialTheme.shape, che ti fornisce l'istanza Shape di accedere alle forme Material.

A molti componenti Material sono già applicate forme predefinite, ma puoi fornire e applicare forme personalizzate ai componenti tramite gli slot disponibili.

Card(shape = MaterialTheme.shapes.medium) { /* card content */ }
FloatingActionButton(shape = MaterialTheme.shapes.large) { /* fab content */}

Valori predefiniti delle forme per tutti i componenti Material 3.Mappatura dei componenti Material utilizzando diversi tipi di forme.

Puoi visualizzare la mappatura delle forme per tutti i componenti nella documentazione relativa alle forme.

Sono disponibili altre due forme da usare, RectangleShape e CircleShape, che fanno parte di Compose. La forma rettangolare non ha raggio del bordo e la forma del cerchio mostra bordi completamente cerchiati.

Puoi anche applicare una forma ai tuoi componenti utilizzando Modifiers che assumono forme, ad esempio Modifier.clip, Modifier.background e Modifier.border.

Forma della barra dell'app

Lo sfondo della barra delle app deve essere arrotondato:

f873392abe535494.png

TopAppBar sta utilizzando un Row con un colore di sfondo. Per ottenere lo sfondo con angolo arrotondato, definisci la forma dello sfondo passando CircleShape al modificatore di sfondo:

ReplyAppBars.kt

@Composable
fun ReplySearchBar(modifier: Modifier = Modifier) {
   Row(
       modifier = modifier
           .fillMaxWidth()
           .padding(16.dp)
           .background(
               MaterialTheme.colorScheme.background,
               CircleShape
           ),
       verticalAlignment = Alignment.CenterVertically
   ) {
       // Search bar content
   }
}

f873392abe535494.png

Forma elemento elenco dettagli

Nella schermata Home, stai utilizzando una carta che utilizza Shape.Medium per impostazione predefinita. Tuttavia, per la nostra pagina dei dettagli, hai utilizzato una colonna con un colore di sfondo. Per un aspetto uniforme, applica una forma media all'elenco.

3412771e95a45f36.png 80ee881c41a98c2a.png

Colonna di dettagli dell'elemento elenco senza forma nell'elemento elenco (sinistra) e forma media nell'elenco (destra).

ReplyEmailThreadItem.kt

@Composable
fun ReplyEmailThreadItem(
   email: Email,
   modifier: Modifier = Modifier
) {
   Column(
       modifier = modifier
           .fillMaxWidth()
           .padding(8.dp)
           .background(
               MaterialTheme.colorScheme.background,
               MaterialTheme.shapes.medium
           )
           .padding(16.dp)

   ) {
      // List item content
      
   }
}

Ora, quando esegui l'app, puoi visualizzare un elenco dettagliato della schermata a forma di medium.

8. Corsivo

L'enfasi nell'interfaccia utente ti aiuta a mettere in evidenza alcuni contenuti rispetto all'altro, ad esempio quando vuoi differenziare il titolo dai sottotitoli. L'enfasi in M3 utilizza variazioni di colore e le sue combinazioni di colori. Esistono due modi per aggiungere enfasi:

  1. Utilizzo dei colori delle varianti di superficie, variante della superficie e sfondo insieme ai colori delle varianti in superficie e in superficie del sistema di colori M3 ampliato.

Ad esempio, la variante superficie può essere utilizzata con la variante in superficie, mentre la variante superficie può essere utilizzata con la variante in superficie per fornire diversi livelli di enfasi.

Le varianti di superficie possono essere utilizzate anche con colori intensi per dare minore enfasi rispetto ai colori in evidenza, pur mantenendo l'accessibilità e rispettando il rapporto di contrasto.

Ruoli dei colori delle varianti di Superficie, Sfondo e Superficie.

Ruoli dei colori di superficie, sfondo e varianti della superficie.

  1. Utilizzare caratteri di spessore diverso per il testo. Come hai visto nella sezione tipografia, puoi fornire pesi personalizzati alla tua scala del tipo per enfasi diversa.

Dopodiché aggiorna ReplyEmailListItem.kt per mettere in evidenza la differenza utilizzando la variante della superficie. Per impostazione predefinita, i contenuti della scheda assumono il colore predefinito dei contenuti, a seconda dello sfondo.

Aggiornerai il colore del componibile del testo dell'ora e del corpo del testo in onSurfaceVariant. In questo modo viene ridotta l'enfasi rispetto a onContainerColors, che viene applicato per impostazione predefinita al testo componibile del soggetto e del titolo.

2c9b7f2bd016edb8.png 6850ff391f21e4ba.png

Ora e corpo del testo con la stessa enfasi rispetto all'oggetto e al titolo (a sinistra).

Tempo e corpo con meno enfasi rispetto al soggetto e al titolo (A destra).

ReplyEmailListItem.kt

Text(
   text = email.createdAt,
   style = MaterialTheme.typography.labelMedium,
   color = MaterialTheme.colorScheme.onSurfaceVariant
)

Text(
   text = email.body,
   maxLines = 2,
   style = MaterialTheme.typography.bodyLarge,
   color = MaterialTheme.colorScheme.onSurfaceVariant
   overflow = TextOverflow.Ellipsis
)

Per la scheda email importanti con sfondo secondaryContainer, tutto il colore del testo è onSecondaryContainer per impostazione predefinita. Per le altre email, lo sfondo è surfaceVariant,, quindi il colore predefinito di tutto il testo è onSurfaceVariant.

9. Complimenti

Complimenti! Hai completato questo codelab correttamente. Hai implementato i temi Material in Compose utilizzando colori, elementi tipografici e forme, oltre a colori dinamici, per definire il tema della tua applicazione e offrire un'esperienza personalizzata.

2d8fcabf15ac5202.png 5a4d31db0185dca6.png ce009e4ce560834d.png

Fine dei risultati della tematizzazione con colori dinamici e tema cromatico applicati.

Passaggi successivi

Dai un'occhiata agli altri nostri codelab sul percorso di scrittura:

Per approfondire

App di esempio

Documenti di riferimento