Gli ampi display aperti e gli esclusivi stati chiusi consentono nuove esperienze utente su dispositivi pieghevoli. Per rendere la tua app sensibile, usa Jetpack WindowManager libreria, che fornisce una piattaforma API per le funzionalità delle finestre pieghevoli del dispositivo come pieghe e cerniere. Quando l'app è sensibile alle pieghe, può adattare il layout per evitare di posizionare contenuti importanti nell'area delle pieghe o delle cerniere e utilizzare le pieghe e le cerniere come separatori naturali.
Capire se un dispositivo supporta configurazioni come da tavolo o libro postura può guidare decisioni relative al supporto di diversi layout o alla fornitura funzionalità specifiche.
Informazioni sulla finestra
L'interfaccia WindowInfoTracker
in Jetpack WindowManager espone le informazioni sul layout della finestra. Il metodo windowLayoutInfo()
dell'interfaccia restituisce un
flusso di dati di WindowLayoutInfo
che informa la tua app di un pieghevole
allo stato fold del dispositivo. Il metodo WindowInfoTracker#getOrCreate()
crea un'istanza
di WindowInfoTracker
.
WindowManager fornisce il supporto per la raccolta dei dati WindowLayoutInfo
utilizzandoflussi Kotlin e callback Java.
Flussi Kotlin
Per avviare e interrompere la raccolta dei dati di WindowLayoutInfo
, puoi utilizzare un riavvio
coroutine sensibile al ciclo di vita in cui il blocco di codice repeatOnLifecycle
viene
eseguito quando il ciclo di vita è di almeno STARTED
e viene interrotto quando
il ciclo di vita è STOPPED
. L'esecuzione del blocco di codice viene riavviata automaticamente quando il ciclo di vita è di nuovo STARTED
. Nell'esempio seguente, il blocco di codice
raccoglie e utilizza i dati di WindowLayoutInfo
:
class DisplayFeaturesActivity : AppCompatActivity() {
private lateinit var binding: ActivityDisplayFeaturesBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDisplayFeaturesBinding.inflate(layoutInflater)
setContentView(binding.root)
lifecycleScope.launch(Dispatchers.Main) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
WindowInfoTracker.getOrCreate(this@DisplayFeaturesActivity)
.windowLayoutInfo(this@DisplayFeaturesActivity)
.collect { newLayoutInfo ->
// Use newLayoutInfo to update the layout.
}
}
}
}
}
Callback Java
Il livello di compatibilità del callback incluso nel
La dipendenza androidx.window:window-java
ti consente di raccogliere
WindowLayoutInfo
si aggiorna senza utilizzare un flusso Kotlin. L'elemento include
la classe WindowInfoTrackerCallbackAdapter
, che adatta
WindowInfoTracker
per supportare la registrazione (e l'annullamento della registrazione) dei callback a
ricevi aggiornamenti WindowLayoutInfo
, ad esempio:
public class SplitLayoutActivity extends AppCompatActivity {
private WindowInfoTrackerCallbackAdapter windowInfoTracker;
private ActivitySplitLayoutBinding binding;
private final LayoutStateChangeCallback layoutStateChangeCallback =
new LayoutStateChangeCallback();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
windowInfoTracker =
new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
}
@Override
protected void onStart() {
super.onStart();
windowInfoTracker.addWindowLayoutInfoListener(
this, Runnable::run, layoutStateChangeCallback);
}
@Override
protected void onStop() {
super.onStop();
windowInfoTracker
.removeWindowLayoutInfoListener(layoutStateChangeCallback);
}
class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
@Override
public void accept(WindowLayoutInfo newLayoutInfo) {
SplitLayoutActivity.this.runOnUiThread( () -> {
// Use newLayoutInfo to update the layout.
});
}
}
}
Supporto RxJava
Se utilizzi già RxJava
(versione 2
o 3
),
puoi sfruttare gli elementi che ti consentono di utilizzare un
Observable
o Flowable
per raccogliere gli aggiornamenti di WindowLayoutInfo
senza utilizzare un flusso Kotlin.
Il livello di compatibilità fornito da androidx.window:window-rxjava2
e
androidx.window:window-rxjava3
dipendenze include
WindowInfoTracker#windowLayoutInfoFlowable()
e
WindowInfoTracker#windowLayoutInfoObservable()
, che ti consentono di
per ricevere aggiornamenti WindowLayoutInfo
, ad esempio:
class RxActivity: AppCompatActivity {
private lateinit var binding: ActivityRxBinding
private var disposable: Disposable? = null
private lateinit var observable: Observable<WindowLayoutInfo>
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// Create a new observable.
observable = WindowInfoTracker.getOrCreate(this@RxActivity)
.windowLayoutInfoObservable(this@RxActivity)
}
@Override
protected void onStart() {
super.onStart();
// Subscribe to receive WindowLayoutInfo updates.
disposable?.dispose()
disposable = observable
.observeOn(AndroidSchedulers.mainThread())
.subscribe { newLayoutInfo ->
// Use newLayoutInfo to update the layout.
}
}
@Override
protected void onStop() {
super.onStop();
// Dispose of the WindowLayoutInfo observable.
disposable?.dispose()
}
}
Caratteristiche dei display pieghevoli
La classe WindowLayoutInfo
di Jetpack WindowManager rende le caratteristiche di un
finestra di visualizzazione disponibile come elenco di elementi DisplayFeature
.
Un FoldingFeature
è un tipo di DisplayFeature
che fornisce informazioni
sui display pieghevoli, tra cui:
state
: lo stato chiuso del dispositivo,FLAT
oHALF_OPENED
.orientation
: l'orientamento della piega o della cerniera,HORIZONTAL
oVERTICAL
occlusionType
: indica se la piega o la cerniera nasconde parte del display,NONE
oFULL
isSeparating
: indica se la piega o la cerniera crea due aree di visualizzazione logiche, true o false
Un dispositivo pieghevole in stato HALF_OPENED
segnala sempre isSeparating
come vero
perché lo schermo è diviso in due aree di visualizzazione. Inoltre, isSeparating
è sempre true su un dispositivo dual screen quando l'applicazione si estende su entrambi gli schermi.
La proprietà FoldingFeature
bounds
(ereditata da DisplayFeature
)
rappresenta il rettangolo di delimitazione di un elemento di piegatura come una piegatura o una cerniera.
È possibile utilizzare i limiti per posizionare gli elementi sullo schermo in relazione alla caratteristica:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { ... lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { // Safely collects from WindowInfoTracker when the lifecycle is // STARTED and stops collection when the lifecycle is STOPPED. WindowInfoTracker.getOrCreate(this@MainActivity) .windowLayoutInfo(this@MainActivity) .collect { layoutInfo -> // New posture information. val foldingFeature = layoutInfo.displayFeatures .filterIsInstance<FoldingFeature>() .firstOrNull() // Use information from the foldingFeature object. } } } }
Java
private WindowInfoTrackerCallbackAdapter windowInfoTracker; private final LayoutStateChangeCallback layoutStateChangeCallback = new LayoutStateChangeCallback(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { ... windowInfoTracker = new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this)); } @Override protected void onStart() { super.onStart(); windowInfoTracker.addWindowLayoutInfoListener( this, Runnable::run, layoutStateChangeCallback); } @Override protected void onStop() { super.onStop(); windowInfoTracker.removeWindowLayoutInfoListener(layoutStateChangeCallback); } class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> { @Override public void accept(WindowLayoutInfo newLayoutInfo) { // Use newLayoutInfo to update the Layout. List<DisplayFeature> displayFeatures = newLayoutInfo.getDisplayFeatures(); for (DisplayFeature feature : displayFeatures) { if (feature instanceof FoldingFeature) { // Use information from the feature object. } } } }
Posizione da tavolo
Utilizzando le informazioni incluse nell'oggetto FoldingFeature
, la tua app può supportare posizioni come da tavolo, in cui lo smartphone è appoggiato su una superficie, la cerniera è in posizione orizzontale e lo schermo pieghevole è semiaperto.
La posizione da tavolo offre agli utenti la comodità di utilizzare i propri smartphone senza doverli tenere in mano. La postura da tavolo è ottima per guardare contenuti multimediali scattare foto e fare videochiamate.

Usa FoldingFeature.State
e FoldingFeature.Orientation
per determinare se il dispositivo è in posizione da tavolo:
Kotlin
fun isTableTopPosture(foldFeature : FoldingFeature?) : Boolean { contract { returns(true) implies (foldFeature != null) } return foldFeature?.state == FoldingFeature.State.HALF_OPENED && foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL }
Java
boolean isTableTopPosture(FoldingFeature foldFeature) { return (foldFeature != null) && (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) && (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL); }
Quando hai verificato che il dispositivo è in modalità da tavolo, aggiorna il layout dell'app di conseguenza. Per le app multimediali, in genere significa posizionare la riproduzione sopra la piega e i controlli e i contenuti supplementari appena sotto per un'esperienza di visualizzazione o ascolto senza mani.
Su Android 15 (livello API 35) e versioni successive, puoi richiamare un'API sincrona per rilevare se un dispositivo supporta la posizione da tavolo indipendentemente dallo stato corrente del dispositivo.
L'API fornisce un elenco di posture supportate dal dispositivo. Se l'elenco contiene la postura di simulazione, puoi suddividere il layout dell'app per supportare la postura ed eseguire test A/B sull'UI dell'app per i layout da tavolo e a schermo intero.
Kotlin
if (WindowSdkExtensions.getInstance().extensionsVersion >= 6) { val postures = WindowInfoTracker.getOrCreate(context).supportedPostures if (postures.contains(TABLE_TOP)) { // Device supports tabletop posture. } }
Java
if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) { List<SupportedPosture> postures = WindowInfoTracker.getOrCreate(context).getSupportedPostures(); if (postures.contains(SupportedPosture.TABLETOP)) { // Device supports tabletop posture. } }
Esempi
App
MediaPlayerActivity
: scopri come utilizzare Media3 Exoplayer e WindowManager per creare un video fold-aware un player.Ottimizzare l'app Fotocamera su dispositivi pieghevoli con Jetpack WindowManager codelab: scopri come implementare la postura da tavolo per le app di fotografia. Mostra il mirino nella metà superiore dello schermo (sopra la piega) e i pulsanti di controllo nella metà inferiore (sotto la piega).
Postura di lettura
Un'altra funzionalità pieghevole unica è la postura del libro, in cui il dispositivo è aperto a metà e la cerniera è verticale. La postura del libro è ideale per leggere gli ebook. Con un layout di due pagine su uno schermo pieghevole di grandi dimensioni aperto come un libro rilegato, la posizione del libro ricrea l'esperienza di lettura di un libro reale.
Può essere usato anche per le foto se vuoi acquisire un aspetto diverso mentre si scattano foto con la voce.
Implementa la postura del libro con le stesse tecniche utilizzate per la postura dei tavoli. L'unica differenza è che il codice deve verificare che l'orientamento della funzionalità di piegatura sia verticale anziché orizzontale:
Kotlin
fun isBookPosture(foldFeature : FoldingFeature?) : Boolean { contract { returns(true) implies (foldFeature != null) } return foldFeature?.state == FoldingFeature.State.HALF_OPENED && foldFeature.orientation == FoldingFeature.Orientation.VERTICAL }
Java
boolean isBookPosture(FoldingFeature foldFeature) { return (foldFeature != null) && (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) && (foldFeature.getOrientation() == FoldingFeature.Orientation.VERTICAL); }
Modifiche alle dimensioni della finestra
L'area di visualizzazione di un'app può cambiare a seguito di una modifica della configurazione del dispositivo, ad esempio quando il dispositivo è chiuso o aperto, ruotato o quando viene modificato il ridimensionamento di una finestra in modalità multi-finestra.
La classe Jetpack WindowManager WindowMetricsCalculator
ti consente di:
recupera le metriche attuali e massime della finestra. Come la piattaforma
WindowMetrics
introdotta nel livello API 30, WindowManager
WindowMetrics
fornisce i limiti della finestra, ma l'API è compatibile con le versioni precedenti
fino al livello API 14.
Consulta Utilizzare le classi di dimensioni della finestra.
Risorse aggiuntive
Campioni
- Jetpack WindowManager: esempio di come utilizzare Jetpack libreria WindowManager
- Jetcaster : implementazione della postura di simulazione con Compose
Codelab
- Supporta dispositivi pieghevoli e a doppio schermo con Jetpack WindowManager
- Ottimizzare l'app Fotocamera sui dispositivi pieghevoli con Jetpack WindowManager