Große, aufgeklappte Displays und einzigartige aufgeklappte Displays ermöglichen neue Nutzererfahrungen auf faltbare Geräte. Wenn Sie Ihre App für faltbare Geräte optimieren möchten, verwenden Sie die Jetpack WindowManager-Bibliothek. Sie bietet eine API-Oberfläche für Fensterfunktionen von faltbaren Geräten wie Scharniere und Falze. Wenn deine App „faltbar“ ist, kann sie ihr Layout anpassen Platzieren Sie wichtige Inhalte nicht im Bereich von Falten oder Scharnieren und verwenden Sie Falten. und Scharniere als natürliche Trennzeichen.
Wenn Sie wissen, ob ein Gerät Konfigurationen wie die Tisch- oder Buchhaltung unterstützt, können Sie Entscheidungen über die Unterstützung verschiedener Layouts oder die Bereitstellung bestimmter Funktionen treffen.
Fensterinformationen
Die WindowInfoTracker
-Oberfläche in Jetpack WindowManager stellt Informationen zum Fensterlayout bereit. Die Methode windowLayoutInfo()
der Schnittstelle gibt ein
Stream von WindowLayoutInfo
-Daten, die deine App über ein faltbares Gerät informieren
des zusammengeklappten Geräts angezeigt. Mit der Methode WindowInfoTracker#getOrCreate()
wird eine Instanz von WindowInfoTracker
erstellt.
WindowManager unterstützt die Erfassung von WindowLayoutInfo
-Daten mithilfe von
Kotlin-Abläufe und Java-Callbacks
Kotlin-Datenflüsse
Um die Datenerhebung für WindowLayoutInfo
zu starten und zu beenden, kannst du eine neustartfähige
lebenszyklusbezogene Koroutine, in der der Codeblock repeatOnLifecycle
wird ausgeführt, wenn der Lebenszyklus mindestens STARTED
beträgt und beendet wird, wenn der
Lebenszyklus ist STOPPED
. Ausführung des Codeblocks wird automatisch neu gestartet
wenn der Lebenszyklus wieder STARTED
ist. Im folgenden Beispiel
enthält der Codeblock
erhebt und verwendet WindowLayoutInfo
-Daten:
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.
}
}
}
}
}
Java-Callbacks
Mit der Callback-Kompatibilitätsebene in der Abhängigkeit androidx.window:window-java
kannst du WindowLayoutInfo
-Updates erfassen, ohne einen Kotlin-Ablauf zu verwenden. Das Artefakt enthält
der WindowInfoTrackerCallbackAdapter
-Klasse, die ein
WindowInfoTracker
, um das Registrieren (und Aufheben der Registrierung) von Rückrufen an
WindowLayoutInfo
-Updates erhalten, zum Beispiel:
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.
});
}
}
}
RxJava-Unterstützung
Wenn Sie RxJava
(Version 2
oder 3
) bereits verwenden,
können Sie Artefakte nutzen, mit denen Sie
Observable
oder Flowable
um WindowLayoutInfo
-Updates ohne Kotlin-Ablauf zu erfassen.
Die von androidx.window:window-rxjava2
und zur Verfügung gestellte Kompatibilitätsebene
androidx.window:window-rxjava3
-Abhängigkeiten enthalten den
WindowInfoTracker#windowLayoutInfoFlowable()
und
WindowInfoTracker#windowLayoutInfoObservable()
-Methoden, mit denen Sie
App, um WindowLayoutInfo
-Updates zu erhalten, zum Beispiel:
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()
}
}
Funktionen faltbarer Displays
Die Klasse WindowLayoutInfo
von Jetpack WindowManager stellt die Funktionen eines
Anzeigefenster als Liste mit DisplayFeature
-Elementen verfügbar.
Ein FoldingFeature
ist eine Art von DisplayFeature
, die Informationen zu faltbaren Displays enthält, darunter:
state
: Der zusammengeklappte Zustand des Geräts,FLAT
oderHALF_OPENED
orientation
: Die Ausrichtung des Scharniers,HORIZONTAL
oderVERTICAL
occlusionType
: Ob das Fold oder Scharnier einen Teil des Displays verdeckt,NONE
oderFULL
isSeparating
: Gibt an, ob durch die Faltung oder das Scharnier zwei logische Darstellungsbereiche erzeugt werden, Wahr oder falsch?
Ein faltbares Gerät, das HALF_OPENED
ist, meldet immer isSeparating
als wahr, da das Display in zwei Displaybereiche unterteilt ist. Außerdem ist isSeparating
ist auf einem Dual Screen-Gerät immer „true“, wenn die App sowohl
Bildschirmen.
Die Property FoldingFeature
bounds
(übernommen von DisplayFeature
)
stellt das Begrenzungsrechteck eines Faltelements dar, z. B. eines Faltelements oder Scharniers.
Anhand der Begrenzungen können Sie Elemente auf dem Bildschirm relativ zur Funktion positionieren:
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. } } } }
Tischaufstellung
Mithilfe der im FoldingFeature
-Objekt enthaltenen Informationen kann Ihre App
wie eine Tischplatte, das Smartphone auf einer Oberfläche liegt, das Scharnier
in horizontaler Position halten und das faltbare Display zur Hälfte aufgeklappt ist.
Mit der Funktion „Auf dem Tisch“ können Nutzende ihr Smartphone ohne das Smartphone in den Händen halten. Die Tischhaltung eignet sich hervorragend zum Ansehen von Medien. Fotos machen und Videoanrufe starten.

Mit FoldingFeature.State
und FoldingFeature.Orientation
können Sie
Ob das Gerät auf dem Tisch steht:
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); }
Wenn Sie wissen, dass sich das Gerät in der Position „Auf dem Tisch“ befindet, aktualisieren Sie das App-Layout entsprechend. Bei Medien-Apps bedeutet dies in der Regel, dass die Wiedergabe über dem Steuerelemente zum Falten und Positionieren sowie ergänzende Inhalte Wiedergabe per Sprachbefehl steuern.
Unter Android 15 (API-Ebene 35) und höher können Sie eine synchrone API aufrufen, um unabhängig vom aktuellen Status des Geräts zu ermitteln, ob es die Positionierung auf einem Tisch unterstützt.
Die API enthält eine Liste der vom Gerät unterstützten Körperhaltungen. Wenn die Liste die Position „Auf dem Tablet“ enthält, können Sie Ihr App-Layout so aufteilen, dass es diese Position unterstützt, und A/B-Tests für die App-Benutzeroberfläche für die Positionen „Auf dem Tablet“ und „Vollbild“ ausführen.
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. } }
Beispiele
MediaPlayerActivity
App: Hier erfahren Sie, wie Sie mit Media3 Exoplayer und WindowManager einen faltbaren Videoplayer erstellen.Kamera-App auf faltbaren Geräten mit Jetpack WindowManager optimieren Codelab: Hier erfahren Sie, wie Sie in Foto-Apps eine Körperhaltung auf dem Tisch platzieren. Der Sucher sollte in der oberen Hälfte des Bildschirms (above the fold) und die Steuerelemente in der unteren Hälfte (below the fold) angezeigt werden.
Buchhaltungsstatus
Eine weitere einzigartige faltbare Funktion ist die Buchhaltung, bei der das Gerät zur Hälfte aufgeklappt ist. und das Scharnier ist vertikal. Die Lesehaltung eignet sich hervorragend zum Lesen von E-Books. Mit einem zweiseitigen Layout auf einem großen Display, das sich wie ein gebundenes Buch öffnen lässt, vermittelt die Buchhaltung das Gefühl, ein echtes Buch zu lesen.
Sie können sie auch für Fotos verwenden, wenn Sie per Sprachbefehl ein anderes Seitenverhältnis festlegen möchten.
Implementieren Sie die Buchhaltung mit denselben Techniken, die auch für die Tischhaltung verwendet werden. Der einzige Unterschied besteht darin, dass der Code prüfen sollte, ob die Ausrichtung der Faltfunktion vertikal statt horizontal ist:
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); }
Änderungen der Fenstergröße
Der Anzeigebereich einer App kann sich aufgrund einer Gerätekonfiguration ändern, z. B. wenn das Gerät auf- oder zugeklappt, gedreht oder ein Fenster im Multifenstermodus neu skaliert wird.
Mit der Jetpack-WindowManager-Klasse WindowMetricsCalculator
können Sie
aktuelle und maximale Fenstermesswerte abrufen. Wie die Plattform
Mit WindowMetrics
in API-Ebene 30 wurde die WindowManager-Funktion
WindowMetrics
stellen die Fenstergrenzen bereit, aber die API ist abwärtskompatibel
bis API-Level 14.
Weitere Informationen finden Sie unter Fenstergrößenklassen verwenden.
Weitere Informationen
Produktproben
- Jetpack WindowManager: Beispiel für die Verwendung von Jetpack WindowManager-Bibliothek
- Jetcaster : Implementierung des Tabellenstatus mit Compose
Codelabs
- Unterstützung für faltbare Smartphones und Geräte mit zwei Displays dank Jetpack WindowManager
- Kamera-App auf faltbaren Geräten mit Jetpack WindowManager optimieren