Il supporto di diverse dimensioni del display consente l'accesso alla tua app dalla più ampia varietà di dispositivi e dal maggior numero di utenti.
Per supportare il maggior numero possibile di dimensioni di visualizzazione, che si tratti di schermi di dispositivi diversi o di finestre di app diverse in modalità multi-finestra, progetta i layout della tua app in modo che siano adattabili e reattivi. I layout adattivi/responsive offrono un'esperienza utente ottimizzata indipendentemente dalle dimensioni del display, consentendo alla tua app di adattarsi a smartphone, tablet, pieghevoli, dispositivi ChromeOS, orientamenti verticale e orizzontale e configurazioni di display ridimensionabili, come la modalità schermo diviso e le finestre delle app.
I layout adattabili cambiano in base allo spazio di visualizzazione disponibile. Le modifiche vanno da piccoli aggiustamenti del layout che riempiono lo spazio (responsive design) alla sostituzione completa di un layout con un altro in modo che l'app possa adattarsi al meglio alle diverse dimensioni del display (adaptive design).
In quanto toolkit UI dichiarativo, Jetpack Compose è ideale per progettare e implementare layout che cambiano dinamicamente per visualizzare i contenuti in modo diverso su schermi di dimensioni diverse.
Competenze Android
Visualizza su GitHubRendi la tua app adattiva
adaptive competenza Android per aggiornare la UI della tua app in modo che si adatti a diversi dispositivi Android e display.android skills add adaptiveRendi esplicite le modifiche di layout di grandi dimensioni per i componibili a livello di contenuti
I componibili a livello di app e di contenuti occupano tutto lo spazio di visualizzazione disponibile per la tua app. Per questi tipi di componibili, potrebbe avere senso modificare il layout complessivo della tua app su schermi di grandi dimensioni.
Evita di utilizzare i valori dell'hardware fisico per prendere decisioni sul layout. Potresti avere la tentazione di prendere decisioni basate su un valore tangibile fisso (il dispositivo è un tablet? Lo schermo fisico ha determinate proporzioni?), ma le risposte a queste domande potrebbero non essere utili per determinare lo spazio disponibile per la tua UI.
Sui tablet, un'app potrebbe essere in esecuzione in modalità multi-finestra, il che significa che potrebbe condividere lo schermo con un'altra app. In modalità finestre delle app o su ChromeOS, un'app potrebbe essere in una finestra ridimensionabile. Potrebbe anche esserci più di uno schermo fisico, come nel caso di un dispositivo pieghevole. In tutti questi casi, le dimensioni fisiche dello schermo non sono rilevanti per decidere come visualizzare i contenuti.
Prendi invece decisioni in base alla porzione effettiva dello schermo assegnata alla tua app descritta dalle metriche della finestra corrente fornite dalla libreria Jetpack WindowManager. Per un esempio di come utilizzare WindowManager in un'app Compose, consulta l'esempio JetNews.
Adattare i layout allo spazio di visualizzazione disponibile riduce anche la quantità di gestione speciale necessaria per supportare piattaforme come ChromeOS e fattori di forma come tablet e pieghevoli.
Dopo aver determinato le metriche dello spazio disponibile per la tua app, converti le dimensioni non elaborate in una classe di dimensioni della finestra, come descritto in Utilizza le classi di dimensioni della finestra. Le classi di dimensioni della finestra sono breakpoint progettati per bilanciare la semplicità della logica dell'app con la flessibilità di ottimizzare l'app per la maggior parte delle dimensioni di visualizzazione.
Le classi di dimensioni della finestra si riferiscono alla finestra complessiva della tua app, quindi utilizzale per le decisioni di layout che influiscono sul layout complessivo dell'app. Puoi trasmettere le classi di dimensioni della finestra come stato o eseguire una logica aggiuntiva per creare uno stato derivato da trasmettere ai componibili nidificati.
@Composable fun MyApp( windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true).windowSizeClass ) { // Decide whether to show the top app bar based on window size class. val showTopAppBar = windowSizeClass.isHeightAtLeastBreakpoint(WindowSizeClass.HEIGHT_DP_MEDIUM_LOWER_BOUND) // MyScreen logic is based on the showTopAppBar boolean flag. MyScreen( showTopAppBar = showTopAppBar, /* ... */ ) }
Un approccio a livelli limita la logica delle dimensioni di visualizzazione a un'unica posizione, anziché distribuirla nell'app in molti punti che devono essere sincronizzati. Una singola posizione produce uno stato, che può essere trasmesso esplicitamente ad altri componibili, proprio come qualsiasi altro stato dell'app. Il passaggio esplicito dello stato semplifica i singoli componibili, perché questi ultimi accettano la classe di dimensioni della finestra o la configurazione specificata insieme ad altri dati.
I componibili nidificati flessibili sono riutilizzabili
I componibili sono più riutilizzabili quando possono essere posizionati in un'ampia varietà di posizioni. Se un composable deve essere posizionato in una posizione specifica con una dimensione specifica, è improbabile che sia riutilizzabile in altri contesti. Ciò significa anche che i singoli componibili riutilizzabili dovrebbero evitare di dipendere implicitamente dalle informazioni sulle dimensioni di visualizzazione globali.
Immagina un componibile nidificato che implementa un layout list-detail, che potrebbe mostrare un singolo riquadro o due riquadri affiancati:
La decisione relativa al layout elenco-dettagli deve far parte del layout complessivo dell'app, quindi viene trasmessa da un componibile a livello di contenuti:
@Composable fun AdaptivePane( showOnePane: Boolean, /* ... */ ) { if (showOnePane) { OnePane(/* ... */) } else { TwoPane(/* ... */) } }
Cosa succede se invece vuoi che un composable modifichi in modo indipendente il proprio layout in base allo spazio di visualizzazione disponibile, ad esempio una scheda che mostra dettagli aggiuntivi se lo spazio lo consente? Vuoi eseguire una logica in base ad alcune dimensioni di visualizzazione disponibili, ma quali dimensioni nello specifico?
Evita di utilizzare le dimensioni dello schermo effettivo del dispositivo. Questo non sarà preciso per i diversi tipi di schermi e non sarà preciso se l'app non è a schermo intero.
Poiché il composable non è un composable a livello di contenuti, non utilizzare direttamente le metriche della finestra corrente.
Se il componente viene posizionato con spaziatura interna (ad esempio con inserti) o se l'app include componenti come barre di navigazione o barre delle app, la quantità di spazio di visualizzazione disponibile per il composable potrebbe differire in modo significativo dallo spazio complessivo disponibile per l'app.
Utilizza la larghezza effettivamente assegnata al componibile per il rendering. Hai due opzioni per ottenere questa larghezza:
Se vuoi modificare dove o come vengono visualizzati i contenuti, utilizza una raccolta di modificatori o un layout personalizzato per rendere il layout adattabile. Ad esempio, un elemento secondario potrebbe riempire tutto lo spazio disponibile oppure gli elementi secondari potrebbero essere disposti su più colonne se lo spazio è sufficiente.
Se vuoi modificare cosa mostrare, utilizza
BoxWithConstraintscome alternativa più efficace.BoxWithConstraintsfornisce vincoli di misurazione che puoi utilizzare per chiamare diversi composabili in base allo spazio di visualizzazione disponibile. Tuttavia, questo comporta un costo, poichéBoxWithConstraintsrimanda la composizione alla fase di layout, quando questi vincoli sono noti, causando un aumento del lavoro da eseguire durante il layout.
@Composable fun Card(/* ... */) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(/* ... */) Title(/* ... */) } } else { Row { Column { Title(/* ... */) Description(/* ... */) } Image(/* ... */) } } } }
Rendi disponibili tutti i dati per diverse dimensioni di visualizzazione
Quando implementi un componibile che sfrutta lo spazio di visualizzazione aggiuntivo, potresti avere la tentazione di essere efficiente e caricare i dati come effetto collaterale delle dimensioni di visualizzazione correnti.
Tuttavia, questa operazione va contro il principio del flusso di dati unidirezionale, in cui i dati possono essere sottoposti a hoisting e forniti ai componibili per il rendering appropriato. Al composable devono essere forniti dati sufficienti in modo che abbia sempre contenuti sufficienti per qualsiasi dimensione di visualizzazione, anche se una parte dei contenuti potrebbe non essere sempre utilizzata.
@Composable fun Card( imageUrl: String, title: String, description: String ) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(imageUrl) Title(title) } } else { Row { Column { Title(title) Description(description) } Image(imageUrl) } } } }
Basandoci sull'esempio di Card, tieni presente che description viene sempre passato a Card. Anche se description viene utilizzato solo quando la larghezza consente di visualizzarlo, Card richiede sempre description, indipendentemente dalla larghezza disponibile.
Il passaggio di contenuti sufficienti semplifica i layout adattivi, rendendoli meno stateful ed evitando di attivare effetti collaterali quando si passa da una dimensione di visualizzazione all'altra (cosa che può verificarsi a causa di un ridimensionamento della finestra, di un cambio di orientamento o della piegatura e apertura di un dispositivo).
Questo principio consente anche di preservare lo stato durante le modifiche del layout. Se esegui l'hoisting delle informazioni che potrebbero non essere utilizzate in tutte le dimensioni di visualizzazione, puoi preservare lo stato dell'app quando le dimensioni del layout cambiano.
Ad esempio, puoi eseguire l'hoisting di un flag booleano showMore in modo che lo stato dell'app venga conservato quando il ridimensionamento del display fa sì che il layout passi dalla visualizzazione dei contenuti alla loro scomparsa:
@Composable fun Card( imageUrl: String, title: String, description: String ) { var showMore by remember { mutableStateOf(false) } BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(imageUrl) Title(title) } } else { Row { Column { Title(title) Description( description = description, showMore = showMore, onShowMoreToggled = { newValue -> showMore = newValue } ) } Image(imageUrl) } } } }
Scopri di più
Per scoprire di più sui layout adattivi in Compose, consulta le seguenti risorse:
App di esempio
- CanonicalLayouts è un repository di pattern di progettazione collaudati che offrono un'esperienza utente ottimale su schermi di grandi dimensioni.
- JetNews mostra come progettare un'app che adatta la sua UI per sfruttare lo spazio di visualizzazione disponibile.
- Reply è un esempio adattivo per supportare dispositivi mobili, tablet e pieghevoli.
- Now in Android è un'app che utilizza layout adattivi per supportare diverse dimensioni del display.
Video