Questa pagina fornisce una panoramica generale dei livelli architetturali che compongono Jetpack Compose e dei principi fondamentali che informano questo design.
Jetpack Compose non è un singolo progetto monolitico, ma è creato da una serie di moduli assemblati insieme 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
- Capire quando puoi passare a un livello inferiore per un maggiore controllo o personalizzazione
- Riduci al minimo le dipendenze
Livelli
I principali livelli di Jetpack Compose sono:
Figura 1. I livelli principali di Jetpack Compose.
Ogni livello si basa su quelli inferiori, combinando le funzionalità per creare componenti di livello superiore. Ogni livello si basa sulle API pubbliche dei livelli inferiori per verificare i limiti del modulo e consentirti di sostituire qualsiasi livello se necessario. Esaminiamo questi livelli dal basso verso l'alto.
- Runtime
- Questo modulo fornisce i principi di base del runtime di Compose, ad esempio
remember
,mutableStateOf
, l'annotazione@Composable
eSideEffect
. Potresti prendere in considerazione la creazione 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 è composto da più moduli (
ui-text
,ui-graphics
,ui-tooling
, ecc.). Questi moduli implementano i concetti fondamentali del toolkit UI, comeLayoutNode
,Modifier
, gestori di input, layout personalizzati e disegno. Potresti prendere in considerazione la creazione di questo livello se hai bisogno solo dei concetti fondamentali di un toolkit UI. - Fondazione
- Questo modulo fornisce componenti di base indipendenti dal sistema di progettazione per l'interfaccia utente Compose,
come
Row
eColumn
,LazyColumn
, il riconoscimento di particolari gesti e così via. Potresti prendere in considerazione la possibilità di basarti sul livello di base per creare il tuo sistema di progettazione. - Materiale
- Questo modulo fornisce un'implementazione del sistema Material Design per l'interfaccia utente Compose, fornendo un sistema di temi, componenti con stile, indicazioni ripple e icone. Crea questo livello quando utilizzi Material Design nella tua app.
Principi di progettazione
Un principio guida di Jetpack Compose è fornire piccole funzionalità 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 la quantità di controllo diretto che hai. Se hai bisogno di un maggiore controllo, puoi 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 iniziasse sempre in grigio, non puoi farlo con questa API. In alternativa, puoi utilizzare l'API
Animatable
di livello inferiore:
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
L'assemblaggio di componenti di livello superiore a partire da blocchi 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
è assemblato 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 disattivatoA
ProvideTextStyle
imposta lo stile di testo predefinito da utilizzareUn
Row
fornisce le norme di layout predefinite per i contenuti del pulsante
Abbiamo omesso alcuni parametri e commenti per rendere più chiara la struttura, ma
l'intero componente è composto solo da circa 40 righe di codice perché assembla semplicemente
questi 4 componenti per implementare il pulsante. Componenti come Button
sono orientati ai parametri che espongono, bilanciando l'attivazione di personalizzazioni comuni con un'esplosione di parametri che possono rendere un componente
più difficile da usare. I componenti Material, ad esempio, offrono personalizzazioni specificate
nel sistema Material Design, semplificando il rispetto dei principi
di Material Design.
Se, tuttavia, vuoi apportare una personalizzazione oltre i parametri di un componente,
puoi "scendere" di un livello e creare una diramazione 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 precedente continua a utilizzare i componenti del livello Material,
come i concetti di Material di
alfa dei contenuti correnti
e lo stile di testo corrente. Tuttavia, sostituisce il materiale Surface
con un
Row
e lo stila per ottenere l'aspetto desiderato.
Se non vuoi utilizzare i concetti di Material, ad esempio se stai creando un sistema di progettazione personalizzato, puoi passare all'utilizzo esclusivo dei 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 livello più alto. Ad esempio,
androidx.compose.material.Text
è basato su
androidx.compose.foundation.text.BasicText
.
In questo modo è possibile fornire la propria implementazione con il nome più
facilmente individuabile se vuoi sostituire i livelli superiori.
Scegliere l'astrazione giusta
La filosofia di Compose di creare componenti a più livelli e riutilizzabili significa che non devi sempre utilizzare i blocchi 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 basarti sul componente di livello più alto che offre la funzionalità di cui hai bisogno per trarre vantaggio dalle best practice che include.
Scopri di più
Consulta l'esempio di Jetsnack per un esempio di creazione di un sistema di progettazione personalizzato.
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Kotlin per Jetpack Compose
- Elenchi e griglie
- Effetti collaterali in Compose