Duże, rozłożone wyświetlacze i wyjątkowe stany złożenia umożliwiają nowe sposoby korzystania z urządzeń składanych. Aby dostosować aplikację do urządzeń składanych, użyj biblioteki Jetpack WindowManager, która udostępnia interfejs API do obsługi funkcji okna urządzenia składanego, takich jak zgięcia i zawiasy. Gdy aplikacja jest dostosowana do urządzeń składanych, może dostosowywać układ, aby uniknąć umieszczania ważnych treści w obszarze zagięć lub zawiasów, a także wykorzystywać zagięcia i zawiasy jako naturalne separatory.
Informacje o tym, czy urządzenie obsługuje konfiguracje takie jak tryb stołowy czy tryb książki, mogą pomóc w podjęciu decyzji o obsłudze różnych układów lub udostępnianiu określonych funkcji.
Informacje o oknie
Interfejs WindowInfoTracker
w bibliotece Jetpack WindowManager udostępnia informacje o układzie okna. Metoda interfejsu windowLayoutInfo()
zwraca strumień danych WindowLayoutInfo
, który informuje aplikację o stanie złożenia urządzenia składanego. Metoda WindowInfoTracker#getOrCreate()
tworzy instancję WindowInfoTracker
.
Biblioteka WindowManager obsługuje zbieranie WindowLayoutInfo
danych za pomocą przepływów Kotlin i wywołań zwrotnych Java.
Przepływy Kotlin
Aby rozpocząć i zatrzymać zbieranie danych WindowLayoutInfo
, możesz użyć koryginy z możliwością ponownego uruchomienia, która uwzględnia cykl życia. Blok kodu repeatOnLifecycle
jest w niej wykonywany, gdy cykl życia osiągnie stan co najmniej STARTED
, a zatrzymywany, gdy cykl życia osiągnie stan STOPPED
. Wykonanie bloku kodu jest automatycznie wznawiane, gdy cykl życia ponownie osiągnie stan STARTED
. W tym przykładzie blok kodu zbiera i wykorzystuje dane 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.
}
}
}
}
}
Wywołania zwrotne w Javie
Warstwa zgodności wywołania zwrotnego zawarta w zależności androidx.window:window-java
umożliwia zbieranie aktualizacji WindowLayoutInfo
bez używania przepływu Kotlin. Artefakt zawiera klasę WindowInfoTrackerCallbackAdapter
, która dostosowuje WindowInfoTracker
, aby obsługiwać rejestrowanie (i wyrejestrowywanie) wywołań zwrotnych w celu otrzymywania aktualizacji WindowLayoutInfo
, np.:
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.
});
}
}
}
Obsługa RxJava
Jeśli używasz już RxJava (wersja 2
lub 3
), możesz korzystać z artefaktów, które umożliwiają używanie Observable
lub Flowable
do zbierania aktualizacji WindowLayoutInfo
bez używania przepływu Kotlin.
Warstwa zgodności udostępniana przez zależności androidx.window:window-rxjava2
i androidx.window:window-rxjava3
zawiera metody WindowInfoTracker#windowLayoutInfoFlowable()
i WindowInfoTracker#windowLayoutInfoObservable()
, które umożliwiają aplikacji otrzymywanie aktualizacji WindowLayoutInfo
, np.:
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()
}
}
Funkcje wyświetlaczy składanych
Klasa WindowLayoutInfo
biblioteki Jetpack WindowManager udostępnia funkcje okna wyświetlania w postaci listy elementów DisplayFeature
.
FoldingFeature
to typ DisplayFeature
, który zawiera informacje o wyświetlaczach składanych, w tym te właściwości:
state
: stan złożenia urządzenia,FLAT
lubHALF_OPENED
orientation
: orientacja zagięcia lub zawiasu,HORIZONTAL
lubVERTICAL
occlusionType
: czy zagięcie lub zawias zasłania część wyświetlacza,NONE
lubFULL
isSeparating
: Czy zagięcie lub zawias tworzy 2 obszary wyświetlania, prawda lub fałsz
Urządzenie składane HALF_OPENED
zawsze zgłasza wartość isSeparating
jako „true”, ponieważ ekran jest podzielony na 2 obszary wyświetlania. W przypadku urządzenia z dwoma ekranami wartość isSeparating
jest zawsze prawdziwa, gdy aplikacja zajmuje oba ekrany.
Właściwość FoldingFeature
bounds
(dziedziczona z DisplayFeature
) reprezentuje prostokąt ograniczający element składany, taki jak zagięcie lub zawias.
Granice można wykorzystać do pozycjonowania elementów na ekranie względem funkcji:
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.
}
}
}
}
Pozycja na stole
Korzystając z informacji zawartych w obiekcie FoldingFeature
, aplikacja może obsługiwać takie pozycje jak tryb stołu, w którym telefon leży na powierzchni, zawias jest w pozycji poziomej, a składany ekran jest otwarty w połowie.
W pozycji stołowej użytkownicy mogą wygodnie obsługiwać telefon bez trzymania go w rękach. Pozycja na stole świetnie się sprawdza podczas oglądania multimediów, robienia zdjęć i prowadzenia rozmów wideo.

Użyj FoldingFeature.State
i FoldingFeature.Orientation
, aby określić, czy urządzenie jest w pozycji stołowej:
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);
}
Gdy urządzenie będzie w pozycji stołowej, odpowiednio zaktualizuj układ aplikacji. W przypadku aplikacji multimedialnych oznacza to zwykle umieszczenie odtwarzania powyżej linii podziału i umieszczenie elementów sterujących oraz treści dodatkowych tuż za nią, aby zapewnić możliwość oglądania lub słuchania bez użycia rąk.
Na Androidzie 15 (poziom interfejsu API 35) i nowszym możesz wywołać synchroniczny interfejs API, aby wykryć, czy urządzenie obsługuje tryb stołowy, niezależnie od jego bieżącego stanu.
Interfejs API udostępnia listę pozycji obsługiwanych przez urządzenie. Jeśli lista zawiera pozycję stołową, możesz podzielić układ aplikacji, aby obsługiwać tę pozycję, i przeprowadzać testy A/B interfejsu aplikacji w przypadku układów stołowych i pełnoekranowych.
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.
}
}
Przykłady
Aplikacja
MediaPlayerActivity
: dowiedz się, jak używać Media3 Exoplayer i WindowManager do tworzenia odtwarzacza wideo dostosowanego do składanego urządzenia.Optymalizacja aplikacji aparatu na urządzeniach składanych za pomocą biblioteki Jetpack WindowManager Codelab: dowiedz się, jak wdrożyć tryb stołowy w aplikacjach do fotografowania. Wyświetl wizjer w górnej połowie ekranu (nad zgięciem), a elementy sterujące w dolnej połowie (pod zgięciem).
Stan rezerwacji
Kolejną unikalną funkcją urządzenia składanego jest tryb książki, w którym urządzenie jest otwarte w połowie, a zawias jest ustawiony pionowo. Tryb książki świetnie sprawdza się podczas czytania e-booków. Układ dwustronicowy na dużym ekranie składanego urządzenia otwartego jak książka w oprawie w trybie książki odzwierciedla wrażenia podczas czytania prawdziwej książki.
Możesz go też używać do robienia zdjęć, jeśli chcesz uchwycić inny format obrazu, robiąc zdjęcia bez użycia rąk.
Wprowadź pozycję książki, stosując te same techniki co w przypadku pozycji na stole. Jedyna różnica polega na tym, że kod powinien sprawdzać, czy orientacja funkcji składania jest pionowa, a nie pozioma:
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);
}
Zmiany rozmiaru okna
Obszar wyświetlania aplikacji może się zmieniać w wyniku zmiany konfiguracji urządzenia, np. gdy urządzenie jest składane lub rozkładane, obracane albo gdy rozmiar okna jest zmieniany w trybie wielu okien.
Klasa Jetpack WindowManager WindowMetricsCalculator
umożliwia pobieranie bieżących i maksymalnych danych okna. Podobnie jak platformaWindowMetrics
wprowadzona na poziomie interfejsu API 30, interfejs WindowManagerWindowMetrics
udostępnia granice okna, ale jest wstecznie zgodny z poziomem interfejsu API 14.
Zobacz Korzystanie z klas rozmiarów okien.
Dodatkowe materiały
Próbki
- Jetpack WindowManager: przykład użycia biblioteki Jetpack WindowManager
- Jetcaster : implementacja postawy przy stole za pomocą Compose
Codelabs
- Obsługa urządzeń składanych i z dwoma ekranami za pomocą Jetpack WindowManager
- Optymalizowanie aplikacji aparatu na urządzeniach składanych za pomocą biblioteki Jetpack WindowManager