Questa pagina fornisce una panoramica generale dei livelli architetturali che compongono Jetpack Compose e dei principi fondamentali alla base di questo design.
Jetpack Compose non è un singolo progetto monolitico, ma è composto da una serie di moduli che vengono 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
- Understand when you can ‘drop down’ to a lower level for more control or customization
- Riduci al minimo le dipendenze
Livelli
I livelli principali 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 confini dei moduli e consentirti di sostituire qualsiasi livello, se necessario. Esaminiamo questi livelli dal basso verso l'alto.
- Runtime
- Questo modulo fornisce i concetti fondamentali del runtime di Compose, come
remember,mutableStateOf, l'annotazione@ComposableeSideEffect. 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 è composto da più moduli (
ui-text,ui-graphics,ui-tooling, ecc.). Questi moduli implementano i concetti di base del toolkit UI, comeLayoutNode,Modifier, gestori di input, layout personalizzati e disegno. Potresti prendere in considerazione l'idea di basarti su questo livello se hai bisogno solo dei concetti fondamentali di un toolkit UI. - Foundation
- Questo modulo fornisce componenti di base indipendenti dal sistema di progettazione per Compose UI, come
RoweColumn,LazyColumn, riconoscimento di gesti particolari e così via. Potresti prendere in considerazione la possibilità di creare il tuo sistema di progettazione basandoti sul livello di base. - Materiale
- Questo modulo fornisce un'implementazione del sistema Material Design per Compose UI, fornendo un sistema di temi, componenti con stile, indicazioni di ripple e icone. Sviluppa su questo livello quando utilizzi Material Design nella tua app.
Design principles
Un principio guida di Jetpack Compose è fornire piccole funzionalità mirate che possono essere assemblate (o composte) insieme, anziché alcuni 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 "scendere" 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 hai bisogno che il componente inizi sempre con il colore 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 è a sua volta basata 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 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
Surfaceche fornisce lo sfondo, la forma, la gestione dei clic e così via.Un
CompositionLocalProviderche modifica l'alpha dei contenuti quando il pulsante è attivato o disattivatoUn elemento
ProvideTextStyleimposta lo stile di testo predefinito da utilizzareUn
Rowfornisce la policy di layout predefinita per i contenuti del pulsante
Abbiamo omesso alcuni parametri e commenti per rendere la struttura più chiara, 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 rigidi riguardo ai parametri che espongono, bilanciando l'abilitazione 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 l'applicazione dei principi di Material Design.
Tuttavia, se vuoi apportare una personalizzazione che va oltre i parametri di un componente, puoi "scendere" di un livello ed 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 con gradiente, 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 alpha dei contenuti correnti e lo stile del testo corrente. Tuttavia, sostituisce il materiale Surface con un Row e lo personalizza per ottenere l'aspetto desiderato.
Se non vuoi utilizzare i concetti di Material, ad esempio se stai creando un sistema di progettazione 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 livello più alto. Ad esempio,
androidx.compose.material.Text
è basato su
androidx.compose.foundation.text.BasicText.
In questo modo, se vuoi sostituire i livelli superiori, puoi fornire la tua implementazione con il nome più rilevabile.
Scegliere l'astrazione giusta
La filosofia di Compose di creare componenti riutilizzabili e stratificati significa che non devi sempre ricorrere ai componenti 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 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 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