Duże rozłożone wyświetlacze i unikalny stan złożenia zwiększają wygodę korzystania z urządzeń składanych. Aby aplikacja zawierała informacje o składaniu aplikacji, użyj biblioteki Jetpack WindowManager, która udostępnia interfejs API do obsługi funkcji w oknie urządzenia składanego, takich jak złożenie czy zawias. Gdy aplikacja jest dobrze widoczna na ekranie, może dostosować swój układ tak, aby uniknąć umieszczania ważnych treści w częściach fałdowych lub zawiasach, i używać zawiasów jako naturalnych separatorów.
Wiedza o tym, czy urządzenie obsługuje konfiguracje takie jak ułożenie na stole czy w księgu, może pomóc w podejmowaniu decyzji o obsługiwaniu różnych układów lub udostępnianiu określonych funkcji.
Informacje o oknie
Interfejs WindowInfoTracker
w Jetpack WindowManager udostępnia informacje o układzie okna. Metoda windowLayoutInfo()
interfejsu zwraca strumień danych WindowLayoutInfo
, który informuje aplikację o stanie składania składanego urządzenia. Metoda WindowInfoTracker#getOrCreate()
tworzy instancję klasy WindowInfoTracker
.
OknoWindowManager obsługuje zbieranie danych WindowLayoutInfo
za pomocą przepływów Kotlin i wywołań zwrotnych w Javie.
Przepływy w Kotlinie
Aby rozpocząć i zatrzymać zbieranie danych WindowLayoutInfo
, możesz użyć przerywanej coroutine z uwzględnieniem cyklu życia, w której blok kodu repeatOnLifecycle
jest wykonywany, gdy cykl życia jest co najmniej STARTED
, i zatrzymywany, gdy cykl życia jest STOPPED
. Wykonywanie bloku kodu jest automatycznie wznawiane, gdy cykl życia jest ponownie w stanie STARTED
. W tym przykładzie blok kodu zbiera i używa danych 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 kodu Kotlin. Artefakt zawiera klasę WindowInfoTrackerCallbackAdapter
, która dostosowuje WindowInfoTracker
do obsługi rejestrowania (i wyrejestrowania) 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 korzystasz już z RxJava
(wersja 2
lub 3
), możesz skorzystać z artefaktów, które umożliwiają gromadzenie aktualizacji WindowLayoutInfo
bez użycia procesu Kotlin za pomocą modelu Observable
lub Flowable
.
Warstwa zgodności udostępniana przez zależności androidx.window:window-rxjava2
i androidx.window:window-rxjava3
obejmuje metody WindowInfoTracker#windowLayoutInfoFlowable()
i WindowInfoTracker#windowLayoutInfoObservable()
, które umożliwiają 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 składanych wyświetlaczy
Klasa WindowLayoutInfo
z poziomu Jetpack WindowManager udostępnia funkcje okna wyświetlania jako listę elementów DisplayFeature
.
FoldingFeature
to typ DisplayFeature
, który zawiera informacje o ekranach składanych, w tym:
state
: stan złożonego urządzenia,FLAT
lubHALF_OPENED
orientation
: orientacja zagięcia lub zawiasów,HORIZONTAL
lubVERTICAL
occlusionType
: czy zawias lub zawias ukrywa część wyświetlacza:NONE
lubFULL
isSeparating
: czy złożenie lub zawias tworzy 2 logiczne obszary wyświetlacza (prawda lub fałsz).
Urządzenie składane w trybie HALF_OPENED
zawsze zwraca wartość isSeparating
jako prawda, ponieważ ekran jest podzielony na 2 obszary wyświetlacza. Ponadto na urządzeniu z dwoma ekranami isSeparating
jest zawsze prawdą, gdy aplikacja obejmuje oba ekrany.
Właściwość FoldingFeature
bounds
(odziedziczona z DisplayFeature
) reprezentuje prostokąt ograniczający element składany, np. zawias.
Za pomocą tych granic możesz umieszczać elementy 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. } } } }
Postawa przy stole
Korzystając z informacji zawartych w obiekcie FoldingFeature
, aplikacja może obsługiwać tryby takie jak stolik, w którym telefon leży na powierzchni, zawias jest w pozycji poziomej, a ekran składany jest w połowie otwarty.
Konstrukcja stołu sprawia, że użytkownicy mogą wygodnie obsługiwać telefon bez trzymania go w rękach. Pozycja na stole jest idealna do oglądania multimediów, robienia zdjęć i prowadzenia rozmów wideo.
Za pomocą FoldingFeature.State
i FoldingFeature.Orientation
możesz określić, czy urządzenie znajduje się w stanie stołu:
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 jest w pozycji poziomej, zaktualizuj układ aplikacji. W przypadku aplikacji multimedialnych oznacza to zwykle umieszczenie odtwarzania nad resztą interfejsu oraz elementów sterujących i treści dodatkowych poniżej, aby umożliwić oglądanie lub słuchanie bez użycia rąk.
W Androidzie 15 (poziom interfejsu API 35) i nowszych możesz wywołać interfejs API synchronicznego, aby wykryć, czy urządzenie obsługuje pozycję na stole niezależnie od jego bieżącego stanu.
Interfejs API zawiera listę stanów obsługiwanych przez urządzenie. Jeśli lista zawiera pozycję stołu, możesz podzielić układ aplikacji, aby uwzględnić tę pozycję, a następnie przeprowadzić testy A/B interfejsu aplikacji w układzie na stół i w układzie pełnoekranowym.
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żyć Media3 Exoplayer i WindowManager do utworzenia odtwarzacza wideo obsługującego składany ekran.Optymalizacja aplikacji aparatu na składanych urządzeniach za pomocą Jetpack WindowManager Codelab: dowiedz się, jak zaimplementować tryb stołu w aplikacjach do fotografowania. Pokaż wizjer w górnej części ekranu (w części strony widocznej na ekranie) i elementy sterujące w dolnej części (w części strony widocznej po przewinięciu).
Stan książki
Kolejną unikalną funkcją składanego urządzenia jest tryb książki, w którym urządzenie jest częściowo otwarte, a zawias jest pionowy. Pozycja książkowa jest świetna do czytania e-booków. Dzięki dwustronicowemu układowi na dużym, składanym ekranie (tak jak w powiązanych książkach) postawa książki uchwyca emocje z prawdziwej książki.
Możesz też używać go do robienia zdjęć, jeśli chcesz robić zdjęcia w innym formacie, nie używając rąk.
Stosuj tę samą technikę, co w przypadku pozycji na stole. Jedyną różnicą jest to, że kod powinien sprawdzać, czy funkcja zwijania ma orientację pionową, a nie poziomą.
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ę zmienić w wyniku zmiany konfiguracji urządzenia, np. gdy urządzenie będzie złożone lub rozłożone, obrócone albo gdy zmienisz rozmiar okna w trybie wielu okien.
Klasa Jetpack WindowManager WindowMetricsCalculator
umożliwia pobieranie bieżących i maksymalnych danych o oknie. Podobnie jak platforma WindowMetrics
wprowadzona na poziomie 30 interfejsu API, WindowManagerWindowMetrics
określa granice okna, ale interfejs API jest zgodny wstecz do poziomu 14.
Zobacz Korzystanie z klas rozmiarów okien.
Dodatkowe materiały
Próbki
- Jetpack WindowManager: przykład użycia biblioteki Jetpack WindowManager
- Jetcaster : implementacja stanu tabel za pomocą Compose
Ćwiczenia z programowania
- Obsługa składanych urządzeń i urządzeń z dwoma ekranami za pomocą Jetpack WindowManager
- Zoptymalizuj aplikację aparatu na urządzeniach składanych dzięki Jetpack WindowManager