Fai conoscere l'app

I grandi display aperti e gli stati piegati univoci consentono nuove esperienze utente sui dispositivi pieghevoli. Per rendere sensibile la tua app, usa la libreria Jetpack WindowManager, che fornisce una piattaforma API per le funzionalità delle finestre del dispositivo pieghevoli, come pieghe e cerniere. Quando l'app è pieghevole, può adattare il suo layout per evitare di posizionare contenuti importanti nelle zone delle pieghe o delle cerniere e utilizzare pieghe e 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 uno stream di dati WindowLayoutInfo che informa l'app dello stato di chiusura di un dispositivo pieghevole. Il metodo WindowInfoTracker getOrCreate() crea un'istanza di WindowInfoTracker.

WindowManager fornisce supporto per la raccolta dei dati WindowLayoutInfo utilizzando Kotlin Flows e i callback Java.

Flussi di Kotlin

Per avviare e interrompere la raccolta dei dati di WindowLayoutInfo, puoi utilizzare una coroutine 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 Kotlin Flow. L'artefatto include la classe WindowInfoTrackerCallbackAdapter, che adatta WindowInfoTracker per supportare la registrazione (e l'annullamento della registrazione) dei callback per ricevere gli 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.

Un FoldingFeature è un tipo di DisplayFeature che fornisce informazioni sui display pieghevoli, tra cui:

  • state: lo stato chiuso del dispositivo, FLAT o HALF_OPENED
  • orientation: l'orientamento della piegatura o della cerniera, HORIZONTAL o VERTICAL
  • occlusionType: se la piega o la cerniera nascondono parte del display, NONE o FULL
  • isSeparating: verifica se la piegatura o la cerniera creano due aree di visualizzazione logiche, vero o falso

Un dispositivo pieghevole HALF_OPENED segnala sempre isSeparating come true perché lo schermo è diviso in due aree di visualizzazione. Inoltre, il criterio isSeparating è sempre valido su un dispositivo con doppio schermo quando l'applicazione si estende su 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 all'elemento.

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 è mezzo aperto.

La modalità da tavolo offre agli utenti la comodità di usare i loro smartphone senza doverlo tenere in mano. La modalità da tavolo è ottima per guardare contenuti multimediali, scattare foto ed effettuare videochiamate.

Un&#39;app di video player in modalità da tavolo

Usa 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 hai stabilito che il dispositivo è in modalità da tavolo, aggiorna il layout della tua app di conseguenza. Per le app multimediali, questo in genere significa posizionare la riproduzione above the fold e i controlli di posizionamento e i contenuti supplementari appena sotto per un'esperienza di visualizzazione o ascolto in vivavoce.

Esempi

Modalità libro

Un'altra postura pieghevole esclusiva è la modalità libro, con il dispositivo aperto a metà e la cerniera verticale. La modalità Libro è ottima per leggere gli ebook. Grazie a un layout a due pagine su un grande schermo pieghevole e aperto come un libro rilegato, la modalità libro acquisisce l'esperienza di lettura di un libro reale.

Può essere utilizzato anche per le foto se vuoi acquisire proporzioni diverse mentre scatti foto a mani libere.

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 della finestra

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 ridimensionato una finestra in modalità multi-finestra.

La classe Jetpack WindowManager WindowMetricsCalculator consente di recuperare le metriche correnti e massime delle finestre. 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.

Vedi Classi di dimensioni delle finestre.

Risorse aggiuntive

Campioni

Codelab