Große Displays im aufgefalteten Zustand und einzigartige Faltzustände ermöglichen neue Nutzererfahrungen auf faltbaren Geräten. Verwende die Jetpack WindowManager-Bibliothek, um deine App mit Auffaltung zu erkennen. Sie bietet eine API-Oberfläche für Fensterfunktionen faltbarer Geräte, z. B. zum Aufklappen und Scharnieren. Wenn Ihre App faltenbewusst ist, kann sie ihr Layout anpassen, um wichtige Inhalte nicht im Bereich von Falzen oder Scharnieren zu platzieren und Falzen und Scharnieren als natürliche Trennlinien zu verwenden.
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 Benutzeroberfläche gibt einen Stream von WindowLayoutInfo
-Daten zurück, der Ihre App über den Faltstatus eines faltbaren Geräts informiert. Mit der Methode WindowInfoTracker#getOrCreate()
wird eine Instanz von WindowInfoTracker
erstellt.
WindowManager unterstützt die Erhebung von WindowLayoutInfo
-Daten mithilfe von Kotlin-Abläufen und Java-Callbacks.
Kotlin-Abläufe
Um die WindowLayoutInfo
-Datenerhebung zu starten und zu beenden, können Sie eine neustartbare, lebenszyklusbewusste Coroutine verwenden, in der der repeatOnLifecycle
-Codeblock ausgeführt wird, wenn der Lebenszyklus mindestens STARTED
beträgt, und angehalten wird, wenn der Lebenszyklus STOPPED
ist. Die Ausführung des Codeblocks wird automatisch fortgesetzt, wenn der Lebenszyklus wieder STARTED
ist. Im folgenden Beispiel werden im Codeblock WindowLayoutInfo
-Daten erfasst und verwendet:
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, die in der androidx.window:window-java
-Abhängigkeit enthalten ist, können Sie WindowLayoutInfo
-Updates erfassen, ohne einen Kotlin-Ablauf zu verwenden. Das Artefakt enthält die Klasse WindowInfoTrackerCallbackAdapter
, die eine WindowInfoTracker
so anpasst, dass Callbacks zum Empfangen von WindowLayoutInfo
-Aktualisierungen registriert (und deregistriert) werden können, z. B.:
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 bereits RxJava
(Version 2
oder 3
) verwenden, können Sie mithilfe von Artefakten einen Observable
oder Flowable
verwenden, um WindowLayoutInfo
-Updates zu erfassen, ohne einen Kotlin-Flow zu verwenden.
Die Kompatibilitätsebene, die von den androidx.window:window-rxjava2
- und androidx.window:window-rxjava3
-Abhängigkeiten bereitgestellt wird, umfasst die Methoden WindowInfoTracker#windowLayoutInfoFlowable()
und WindowInfoTracker#windowLayoutInfoObservable()
, mit denen Ihre App WindowLayoutInfo
-Updates erhalten kann, z. B.:
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 von faltbaren Displays
Die WindowLayoutInfo
-Klasse von Jetpack WindowManager stellt die Funktionen eines Anzeigefensters als Liste von DisplayFeature
-Elementen bereit.
Eine 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
: Ausrichtung der Faltung oder des ScharniersHORIZONTAL
oderVERTICAL
occlusionType
: Ob das Scharnier oder die Falte einen Teil des Displays verdeckt,NONE
oderFULL
isSeparating
: Gibt an, ob durch das Scharnier zwei logische Displaybereiche entstehen, „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
auf einem Dual-Screen-Gerät immer „wahr“, wenn die App beide Bildschirme umfasst.
Die Eigenschaft FoldingFeature
bounds
(übernommen von DisplayFeature
) stellt das Begrenzungsrechteck eines Faltelements wie Faltfunktion oder Scharnier dar.
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
Anhand der Informationen im FoldingFeature
-Objekt kann Ihre App Positionen wie „Auf dem Tisch“ unterstützen, bei der das Smartphone auf einer Oberfläche liegt, das Scharnier in einer horizontalen Position ist und das faltbare Display halb geöffnet ist.
In dieser Position können Nutzer ihr Smartphone bequem bedienen, ohne es in der Hand halten zu müssen. Diese Position eignet sich hervorragend, um sich Medien anzusehen, Fotos aufzunehmen und Videoanrufe zu führen.
Mit FoldingFeature.State
und FoldingFeature.Orientation
kannst du feststellen, ob das Gerät auf „Auf dem Tisch“ gestellt wird:
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 das in der Regel, dass die Wiedergabe über dem Falz platziert und Steuerelemente und zusätzliche Inhalte direkt darunter platziert werden, damit Inhalte ohne Berühren des Displays angesehen oder angehört werden können.
Unter Android 15 (API-Level 35) und höher können Sie eine synchrone API aufrufen, um unabhängig vom aktuellen Gerätestatus festzustellen, ob ein Gerät „Auf dem 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-UI 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.Optimiere deine Kamera-App auf faltbaren Geräten mit dem Jetpack WindowManager-Codelab: Hier erfährst du, wie du für Foto-Apps einen „Auf dem Tisch“ verwendest. Der Sucher sollte sich in der oberen Hälfte des Bildschirms (above the fold) und die Steuerelemente in der unteren Hälfte (below the fold) befinden.
Buchstatus
Eine weitere einzigartige Funktion des faltbaren Geräts ist die Buchposition, bei der das Gerät halb geöffnet ist und das Scharnier vertikal steht. Die Buchhaltung eignet sich hervorragend zum Lesen von E-Books. Mit dem zweiseitigen Layout auf einem faltbaren Großbildschirm, der wie ein gebundenes Buch aufgeklappt ist, wird das Lesen eines echten Buchs erlebt.
Sie können sie auch für Fotos verwenden, wenn Sie per Sprachbefehl ein anderes Seitenverhältnis festlegen möchten.
Verwenden Sie für die Buchhaltung dieselben Techniken wie für die Tischhaltung. Der einzige Unterschied besteht darin, dass im Code geprüft werden sollte, ob das Faltelement vertikal statt horizontal ausgerichtet 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-Klasse „WindowManager“ WindowMetricsCalculator
können Sie die aktuellen und maximalen Fenstermesswerte abrufen. Wie die Plattform WindowMetrics
, die in API-Ebene 30 eingeführt wurde, gibt auch der WindowManagerWindowMetrics
die Fenstergrenzen an. Die API ist jedoch abwärtskompatibel bis API-Ebene 14.
Weitere Informationen finden Sie unter Fenstergrößenklassen verwenden.
Weitere Informationen
Produktproben
- Jetpack WindowManager: Beispiel für die Verwendung der Jetpack-WindowManager-Bibliothek
- Jetcaster : Implementierung der Position „Auf dem Tisch“ mit Compose
Codelabs
- Unterstützung für faltbare Smartphones und Geräte mit zwei Displays dank Jetpack WindowManager
- Kamera-App mit Jetpack WindowManager für faltbare Geräte optimieren