Dank des großen aufgeklappten Displays und des aufgeklappten Zustands kannst du die Nutzung auf faltbaren Geräten noch weiter verbessern. Verwenden Sie die Bibliothek Jetpack WindowManager, um eine API-Oberfläche für Fensterfunktionen faltbarer Geräte wie das Aufklappen und das Scharnier zu erstellen, damit Ihre App faltbar ist. Wenn deine App faltbar ist, kann sie ihr Layout so anpassen, dass wichtige Inhalte nicht im Bereich der Falten oder Scharniere platziert werden, und Falten und Scharniere als natürliche Trennelemente verwendet werden.
Fensterinformationen
Über die WindowInfoTracker
-Schnittstelle in Jetpack WindowManager werden Informationen zum Fensterlayout angezeigt. Die Methode windowLayoutInfo()
der Benutzeroberfläche gibt einen Stream von WindowLayoutInfo
-Daten zurück, die Ihre App über den faltbaren Zustand eines faltbaren Geräts informieren. Die Methode WindowInfoTracker
getOrCreate()
erstellt eine Instanz von WindowInfoTracker
.
WindowManager unterstützt die Erfassung von WindowLayoutInfo
-Daten mithilfe von Kotlin-Abläufen und Java-Callbacks.
Kotlin-Datenflüsse
Wenn Sie die Datenerhebung durch WindowLayoutInfo
starten und beenden möchten, können Sie eine neustartbare lebenszyklusfähige Koroutine verwenden, in der der Codeblock repeatOnLifecycle
ausgeführt wird, wenn der Lebenszyklus mindestens STARTED
ist, und beendet, wenn der Lebenszyklus STOPPED
ist. Die Ausführung des Codeblocks wird automatisch neu gestartet, wenn der Lebenszyklus wieder STARTED
ist. Im folgenden Beispiel erfasst und verwendet der Codeblock 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, die in der androidx.window:window-java
-Abhängigkeit enthalten ist, können Sie WindowLayoutInfo
-Updates ohne Kotlin-Ablauf erfassen. Das Artefakt enthält die Klasse WindowInfoTrackerCallbackAdapter
, die einen WindowInfoTracker
anpasst, um das Registrieren (und Aufheben der Registrierung) von Callbacks zu unterstützen, um WindowLayoutInfo
-Updates zu erhalten. 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 bereits RxJava
(Version 2
oder 3
) verwenden, können Sie Artefakte nutzen, die es Ihnen ermöglichen, mit Observable
oder Flowable
WindowLayoutInfo
Updates ohne Kotlin-Ablauf zu erfassen.
Die von den androidx.window:window-rxjava2
- und androidx.window:window-rxjava3
-Abhängigkeiten bereitgestellte Kompatibilitätsebene umfasst die Methoden WindowInfoTracker#windowLayoutInfoFlowable()
und WindowInfoTracker#windowLayoutInfoObservable()
, mit denen Ihre App WindowLayoutInfo
-Updates erhalten kann. 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 the WindowLayoutInfo observable
disposable?.dispose()
}
}
Funktionen faltbarer Displays
Die Klasse WindowLayoutInfo
von Jetpack WindowManager stellt die Funktionen eines Anzeigefensters als Liste von DisplayFeature
-Elementen zur Verfügung.
Ein FoldingFeature
ist ein DisplayFeature
-Typ, der Informationen zu faltbaren Displays liefert, darunter:
state
: der zugeklappte Zustand des Geräts,FLAT
oderHALF_OPENED
orientation
: Ausrichtung des Fold- oder Scharniers,HORIZONTAL
oderVERTICAL
occlusionType
: Ob durch das Falt- oder Scharnier ein Teil des Displays verdeckt wird,NONE
oderFULL
isSeparating
: Gibt an, ob durch das Falten oder Scharnier zwei logische Displaybereiche entstehen – richtig oder falsch
Ein faltbares Gerät, das HALF_OPENED
ist, meldet isSeparating
immer als „true“, da der Bildschirm in zwei Displaybereiche unterteilt ist. Außerdem ist isSeparating
auf einem Gerät mit Dual Screen immer „true“, wenn die App beide Bildschirme umfasst.
Die Eigenschaft FoldingFeature
bounds
(übernommen von DisplayFeature
) steht für das Begrenzungsrechteck einer Faltfunktion, z. B. einer Faltung oder einem Scharnier. Die Begrenzungen können verwendet werden, um Elemente auf dem Bildschirm relativ zum Element zu positionieren.
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 } } } }
Modus „Auf dem Tisch“
Anhand der Informationen im FoldingFeature
-Objekt kann deine App Funktionen wie den Modus „Auf dem Tisch“ unterstützen, bei dem das Smartphone auf einer Oberfläche steht, das Scharnier in horizontaler Position ist und der faltbare Bildschirm zur Hälfte geöffnet ist.
Mit dem Modus „Auf dem Tisch“ können Nutzer ihr Smartphone bequem bedienen, ohne es in der Hand zu halten. Der Modus „Auf dem Tisch“ eignet sich perfekt, um Medien anzusehen, Fotos zu machen und Videoanrufe zu starten.
Mit FoldingFeature.State
und FoldingFeature.Orientation
können Sie feststellen, ob sich das Gerät im Modus „Auf dem Tisch“ befindet:
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); }
Sobald Sie wissen, dass sich das Gerät im Modus „Auf dem Tisch“ befindet, passen Sie das App-Layout entsprechend an. Bei Medien-Apps bedeutet das in der Regel, dass die Wiedergabe „above the fold“ (ohne Scrollen sichtbar) platziert und die Steuerelemente und ergänzenden Inhalte direkt darunter platziert werden, um sie per Sprachbefehl anzusehen oder anzuhören.
Beispiele
MediaPlayerActivity
App: Hier erfahren Sie, wie Sie mit Media3 Exoplayer und WindowManager einen Videoplayer erstellen, der auf den Nutzer zugeschnitten ist.Codelab zum Aufklappen der Kamera: Hier erfahren Sie, wie Sie den Modus „Auf dem Tisch“ für Foto-Apps implementieren. Zeigen Sie den Sucher in der oberen Hälfte des Bildschirms, „above the fold“ (ohne Scrollen sichtbar) und die Steuerelemente in der unteren Hälfte, „below the fold“.
Buchmodus
Ein weiterer einzigartiger faltbarer Modus ist der Buchmodus, in dem das Gerät zur Hälfte aufgeklappt und das Scharnier vertikal ist. Der Buchmodus eignet sich hervorragend zum Lesen von E-Books. Dank des zweiseitigen Layouts auf einem faltbaren Bildschirm, das wie ein gebundenes Buch geöffnet ist, eignet sich der Buchmodus genau wie das Lesen eines echten Buches.
Die Kamera kann auch für Fotos verwendet werden, wenn du ein anderes Seitenverhältnis aufnehmen möchtest, während du per Sprachbefehl Fotos aufnehmen möchtest.
Implementieren Sie den Buchmodus mit denselben Techniken wie im Modus „Auf dem Tisch“. Der einzige Unterschied besteht darin, dass der Code prüfen sollte, ob die Ausrichtung des Faltelements 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 durch eine Änderung der Gerätekonfiguration ändern, wenn das Gerät beispielsweise zugeklappt oder aufgeklappt oder gedreht oder die Größe eines Fensters im Mehrfenstermodus geändert wird.
Mit der Jetpack WindowManager-Klasse WindowMetricsCalculator
können Sie die aktuellen und maximalen Fenstermesswerte abrufen. Wie bei der in API-Level 30 eingeführten Plattform WindowMetrics
stellt auch der WindowManager WindowMetrics
die Fenstergrenzen bereit, aber die API ist abwärtskompatibel bis hin zum API-Level 14.
Weitere Informationen zur Unterstützung verschiedener Fenstergrößen finden Sie unter Unterstützung verschiedener Bildschirmgrößen.
Weitere Informationen
Produktproben
- Jetpack WindowManager: Beispiel zur Verwendung der Jetpack WindowManager-Bibliothek
- Jetcaster: Implementierung des „Auf dem Tisch“-Status mit Compose
Codelabs
- Unterstützung von faltbaren Geräten und Geräten mit Dual Screen und Jetpack WindowManager
- Klappe deine Kamera auf