I display grandi aperti e gli stati piegati univoci consentono nuove esperienze utente sui dispositivi pieghevoli. Per rendere la tua app pieghevole, utilizza la libreria Jetpack WindowManager, che fornisce una piattaforma API per le funzionalità delle finestre dei dispositivi pieghevoli, ad esempio pieghe e cerniere. Quando l'app è pieghevole, può adattare il suo layout per evitare di inserire contenuti importanti nelle aree delle pieghe o delle cerniere e utilizzare le pieghe e le cerniere come separatori naturali.
Informazioni sulla finestra
L'interfaccia WindowInfoTracker
in Jetpack WindowManager mostra le informazioni sul layout delle finestre. Il metodo windowLayoutInfo()
dell'interfaccia restituisce un flusso di dati WindowLayoutInfo
che informa la tua app dello stato di chiusura di un dispositivo pieghevole. Il metodo WindowInfoTracker
getOrCreate()
crea un'istanza di WindowInfoTracker
.
WindowManager offre supporto per la raccolta di dati WindowLayoutInfo
utilizzando Kotlin Flows e i callback Java.
Flussi Kotlin
Per avviare e arrestare la raccolta dei dati di WindowLayoutInfo
, puoi utilizzare una coroutine riavviabile sensibile al ciclo di vita in cui il blocco di codice repeatOnLifecycle
viene eseguito quando il ciclo di vita è 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 nella dipendenza androidx.window:window-java
ti consente di raccogliere aggiornamenti WindowLayoutInfo
senza utilizzare un flusso Kotlin. L'artefatto include la classe WindowInfoTrackerCallbackAdapter
, che adatta un WindowInfoTracker
per supportare la registrazione (e l'annullamento della registrazione) dei callback per ricevere 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 per RxJava
Se utilizzi già RxJava
(versione 2
o 3
), puoi sfruttare gli elementi che ti consentono di utilizzare Observable
o Flowable
per raccogliere aggiornamenti WindowLayoutInfo
senza utilizzare un Kotlin Flow.
Il livello di compatibilità fornito dalle dipendenze androidx.window:window-rxjava2
e androidx.window:window-rxjava3
include i metodi WindowInfoTracker#windowLayoutInfoFlowable()
e WindowInfoTracker#windowLayoutInfoObservable()
, che consentono all'app di 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 the WindowLayoutInfo observable
disposable?.dispose()
}
}
Caratteristiche dei display pieghevoli
La classe WindowLayoutInfo
di Jetpack WindowManager rende disponibili le funzionalità di una finestra di visualizzazione sotto forma di elenco di elementi DisplayFeature
.
Una 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 piegatura o della cerniera,HORIZONTAL
oVERTICAL
occlusionType
: se la cerniera o la cerniera nascondono parte del display,NONE
oFULL
isSeparating
: se la cerniera o la cerniera creano due aree di visualizzazione logiche, true o false
Un dispositivo pieghevole HALF_OPENED
segnala sempre isSeparating
come true perché lo schermo è separato in due aree di visualizzazione. Inoltre, il valore isSeparating
è sempre true su un dispositivo con doppio schermo quando l'applicazione copre entrambi gli schermi.
La proprietà FoldingFeature
bounds
(ereditata da DisplayFeature
) rappresenta il rettangolo di delimitazione di una funzionalità di piegatura come una piegatura o una cerniera. I limiti possono essere utilizzati per posizionare gli elementi sullo schermo in relazione alla funzionalità.
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { ... lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { // Safely collects from windowInfoRepo 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() .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 } } } }
Modalità da tavolo
Utilizzando le informazioni incluse nell'oggetto FoldingFeature
, la tua app può supportare posizioni quali la modalità da tavolo, in cui il telefono è appoggiato su una superficie, la cerniera è in posizione orizzontale e lo schermo pieghevole è semiaperto.
La modalità da tavolo offre agli utenti la comodità di utilizzare i loro smartphone senza tenere il telefono in mano. La modalità Da tavolo è ottima per guardare contenuti multimediali, scattare foto ed effettuare videochiamate.
Utilizza FoldingFeature.State
e FoldingFeature.Orientation
per determinare se il dispositivo è in modalità 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 sai che il dispositivo è in modalità da tavolo, aggiorna il layout della tua app di conseguenza. Per le app multimediali, in genere ciò significa posizionare la riproduzione above the fold, i controlli di posizionamento e i contenuti aggiuntivi appena sotto per un'esperienza di visualizzazione o ascolto a mani libere.
Esempi
App
MediaPlayerActivity
: scopri come utilizzare Media3 Exoplayer e WindowManager per creare un video player pieghevole.Codelab Scopri l'esperienza con la fotocamera: scopri come implementare la modalità da tavolo per le app di fotografia. Mostra il mirino nella metà superiore dello schermo, above the fold, e i controlli nella metà inferiore, below the fold.
Modalità libro
Un'altra struttura pieghevole unica è la modalità libro, in cui il dispositivo è semiaperto e la cerniera è verticale. La modalità Libro è ideale per leggere ebook. Con un layout a due pagine su un grande schermo pieghevole aperto come un libro rilegato, la modalità libro cattura l'esperienza di lettura di un libro reale.
Può essere utilizzato anche per la fotografia se vuoi acquisire proporzioni diverse mentre scatti le foto con la voce.
Implementa la modalità libro con le stesse tecniche utilizzate per la modalità da tavolo. 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 delle finestre
L'area di visualizzazione di un'app può cambiare in seguito a una modifica della configurazione del dispositivo, ad esempio quando il dispositivo viene piegato o aperto, ruotato o una finestra viene ridimensionata in modalità multi-finestra.
La classe Jetpack WindowManager WindowMetricsCalculator
consente di recuperare le metriche relative alle finestre correnti e massime. Come la piattaforma WindowMetrics
introdotta nel livello API 30, WindowManager WindowMetrics
fornisce i limiti delle finestre, ma l'API è compatibile con le versioni precedenti fino al livello 14.
Scopri come supportare finestre di dimensioni diverse in Supportare schermi di dimensioni diverse.
Risorse aggiuntive
Samples
- Jetpack WindowManager: esempio di come utilizzare la libreria Jetpack WindowManager
- Jetcaster: implementazione della postura da tavolo con Compose