Questa pagina fornisce una panoramica generale dei livelli di architettura che compongono Jetpack Compose e dei principi fondamentali alla base di questo design.
Jetpack Compose non è un singolo progetto monolitico; viene creato da un numero di moduli assemblati per formare uno stack completo. Comprendere i diversi moduli che compongono Jetpack Compose ti consente di:
- Utilizza il livello di astrazione appropriato per creare l'app o la libreria
- Scopri quando puoi "scendere" a un livello inferiore per avere un maggiore controllo o personalizzazione
- Riduci al minimo le dipendenze
Livelli
I principali livelli di Jetpack Compose sono:
Figura 1. I principali livelli di Jetpack Compose.
Ogni livello si basa sui livelli inferiori, combinando le funzionalità per creare componenti di livello superiore. Ogni livello si basa sulle API pubbliche dei livelli inferiori per verificare i confini del modulo e consentirti di sostituire qualsiasi livello, se necessario. Esaminiamo questi livelli dal basso verso l'alto.
- Tempo di esecuzione
- Questo modulo fornisce le nozioni di base del runtime di Compose, ad esempio
remember
,mutableStateOf
, l'annotazione@Composable
eSideEffect
. Potresti prendere in considerazione la possibilità di creare direttamente su questo livello se hai bisogno solo delle funzionalità di gestione dell'albero di Compose, non della sua UI. - Interfaccia utente
- Il livello UI è costituito da più moduli (
ui-text
,ui-graphics
,ui-tooling
e così via). Questi moduli implementano le nozioni di base del toolkit UI, ad esempioLayoutNode
,Modifier
, gestori di input, layout personalizzati e disegno. Ti consigliamo di utilizzare questo livello se hai bisogno solo dei concetti fondamentali di un kit di strumenti per l'interfaccia utente. - Foundation
- Questo modulo fornisce componenti di base indipendenti dal sistema di progettazione per l'interfaccia utente di Compose, come
Row
eColumn
,LazyColumn
, il riconoscimento di determinati gesti e così via. Ti consigliamo di utilizzare il livello di base per creare il tuo sistema di progettazione. - Materiale
- Questo modulo fornisce un'implementazione del sistema Material Design per l'interfaccia utente di Compose, offrendo un sistema di temi, componenti stilizzati, indicazioni di effetto ripple e icone. Basati su questo livello quando utilizzi Material Design nella tua app.
Principi di progettazione
Un principio guida di Jetpack Compose è fornire funzionalità piccole e mirate che possono essere assemblate (o composte) insieme, anziché pochi componenti monolitici. Questo approccio presenta una serie di vantaggi.
Controllo
I componenti di livello superiore tendono a fare di più per te, ma limitano il controllo diretto che hai. Se hai bisogno di un maggiore controllo, puoi "abbassarti" per utilizzare un componente di livello inferiore.
Ad esempio, se vuoi animare il colore di un componente, puoi utilizzare l'API
animateColorAsState
:
val color = animateColorAsState(if (condition) Color.Green else Color.Red)
Tuttavia, se avevi bisogno che il componente fosse sempre grigio all'avvio, non puoi farlo con questa API. In alternativa, puoi utilizzare l'API di livello inferiore
Animatable
:
val color = remember { Animatable(Color.Gray) } LaunchedEffect(condition) { color.animateTo(if (condition) Color.Green else Color.Red) }
L'API animateColorAsState
di livello superiore si basa sull'API Animatable
di livello inferiore. L'utilizzo dell'API di livello inferiore è più complesso, ma offre un maggiore controllo. Scegli il livello di astrazione più adatto alle tue esigenze.
Personalizzazione
Assemblare componenti di livello superiore da elementi di base più piccoli semplifica notevolmente la personalizzazione dei componenti, se necessario. Ad esempio, considera l'implementazione di Button
fornita dal livello Material:
@Composable fun Button( // … content: @Composable RowScope.() -> Unit ) { Surface(/* … */) { CompositionLocalProvider(/* … */) { // set LocalContentAlpha ProvideTextStyle(MaterialTheme.typography.button) { Row( // … content = content ) } } } }
Un Button
è composto da 4 componenti:
Un materiale
Surface
che fornisce lo sfondo, la forma, la gestione dei clic e così via.Un
CompositionLocalProvider
che modifica l'alpha dei contenuti quando il pulsante è attivato o disattivatoUn
ProvideTextStyle
imposta lo stile di testo predefinito da utilizzareUn
Row
fornisce il criterio di layout predefinito per i contenuti del pulsante
Abbiamo omesso alcuni parametri e commenti per rendere la struttura più chiara, ma
l'intero componente è composto da circa 40 righe di codice perché assembla semplicemente questi 4 componenti per implementare il pulsante. Componenti come Button
hanno un'opinione su quali parametri esporre, bilanciando l'attivazione di personalizzazioni comuni con un'esplosione di parametri che può rendere un componente più difficile da utilizzare. I componenti Material, ad esempio, offrono personalizzazioni specificate nel sistema Material Design, semplificando l'applicazione dei principi di Material Design.
Tuttavia, se vuoi apportare una personalizzazione oltre i parametri di un componente, puoi "scendere" di un livello e eseguire il fork di un componente. Ad esempio, Material Design specifica che i pulsanti devono avere uno sfondo a tinta unita. Se hai bisogno di uno sfondo sfumato, questa opzione non è supportata dai parametri Button
. In questo caso, puoi utilizzare l'implementazione di Material Button
come riferimento e creare il tuo componente:
@Composable fun GradientButton( // … background: List<Color>, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Row( // … modifier = modifier .clickable(onClick = {}) .background( Brush.horizontalGradient(background) ) ) { CompositionLocalProvider(/* … */) { // set material LocalContentAlpha ProvideTextStyle(MaterialTheme.typography.button) { content() } } } }
L'implementazione riportata sopra continua a utilizzare i componenti del livello Material, come i concetti di Material relativi all'alpha dei contenuti correnti e allo stile di testo corrente. Tuttavia, sostituisce il materiale Surface
con un
Row
e lo stilizza per ottenere l'aspetto desiderato.
Se non vuoi utilizzare i concetti di Material, ad esempio se stai creando il tuo sistema di design personalizzato, puoi utilizzare solo i componenti del livello di base:
@Composable fun BespokeButton( // … backgroundColor: Color, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Row( // … modifier = modifier .clickable(onClick = {}) .background(backgroundColor) ) { // No Material components used content() } }
Jetpack Compose riserva i nomi più semplici per i componenti di primo livello. Ad esempio,
androidx.compose.material.Text
è basato su
androidx.compose.foundation.text.BasicText
.
In questo modo, puoi fornire la tua implementazione con il nome più facilmente rilevabile se vuoi sostituire i livelli superiori.
Scegliere l'astrazione giusta
La filosofia di Compose, che consiste nel creare componenti riutilizzabili e a più livelli, significa che non devi sempre utilizzare gli elementi di base di livello inferiore. Molti componenti di livello superiore non solo offrono più funzionalità, ma spesso implementano best practice come il supporto dell'accessibilità.
Ad esempio, se vuoi aggiungere il supporto dei gesti al tuo componente personalizzato, puoi crearlo da zero utilizzando Modifier.pointerInput
, ma esistono altri componenti di livello superiore basati su questo che potrebbero offrire un punto di partenza migliore, ad esempio Modifier.draggable
, Modifier.scrollable
o Modifier.swipeable
.
Come regola generale, preferisci creare il componente di livello più alto che offre la funzionalità di cui hai bisogno per usufruire delle best practice incluse.
Scopri di più
Consulta l'esempio Jetsnack per un esempio di creazione di un sistema di design personalizzato.
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Kotlin per Jetpack Compose
- Elenchi e griglie
- Effetti collaterali in Componi