Duże, rozłożone wyświetlacze i unikalne stany złożenia umożliwiają korzystanie z nowych funkcji na urządzeniach składanych. Aby aplikacja była dostosowana do składania, użyj biblioteki Jetpack WindowManager, która udostępnia interfejs API do obsługi funkcji okien na urządzeniach składanych takich jak zagięcia i zawiasy. Gdy aplikacja jest dostosowana do składania, może dostosować swój układ, aby nie umieszczać ważnych treści w obszarze zagięć lub zawiasów, i używać zagięć oraz zawiasów jako naturalnych separatorów.
Informacje o tym, czy urządzenie obsługuje konfiguracje takie jak tryb stołu lub tryb książki, mogą pomóc w podejmowaniu decyzji o obsłudze 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() tego interfejsu zwraca
strumień danych WindowLayoutInfo, który informuje aplikację o stanie złożenia urządzenia składanego. Metoda WindowInfoTracker#getOrCreate() tworzy instancję WindowInfoTracker.
WindowManager obsługuje zbieranie danych WindowLayoutInfo za pomocą Kotlin flows i wywołań zwrotnych Java.
Kotlin flows
Aby rozpocząć i zatrzymać zbieranie danych WindowLayoutInfo, możesz użyć współprogramu z możliwością ponownego uruchomienia, który jest świadomy cyklu życia. Blok kodu repeatOnLifecycle jest wykonywany, gdy cykl życia jest co najmniej w stanie STARTED, i zatrzymywany, gdy cykl życia jest w stanie 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 : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { // ... 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 Java
Warstwa zgodności wywołań zwrotnych zawarta w zależności
androidx.window:window-java umożliwia zbieranie
WindowLayoutInfo aktualizacji bez używania Kotlin flow. Artefakt zawiera
klasę WindowInfoTrackerCallbackAdapter, która dostosowuje
WindowInfoTracker do obsługi rejestrowania (i wyrejestrowywania) wywołań zwrotnych w celu
otrzymywania aktualizacji WindowLayoutInfo, na przykład:
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 (w wersji 2 lub 3),
możesz skorzystać z artefaktów, które umożliwiają używanie
Observable lub Flowable
do zbierania aktualizacji WindowLayoutInfo bez używania Kotlin flow.
Warstwa zgodności udostępniana przez androidx.window:window-rxjava2 i
androidx.window:window-rxjava3 zależności zawiera metody
WindowInfoTracker#windowLayoutInfoFlowable() i
WindowInfoTracker#windowLayoutInfoObservable(), które umożliwiają
aplikacji otrzymywanie WindowLayoutInfo aktualizacji, na przykład:
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 = ActivityRxBinding.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 w Jetpack WindowManager udostępnia funkcje okna wyświetlacza jako listę 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,FLATlubHALF_OPENEDorientation: orientacja zagięcia lub zawiasu,HORIZONTALlubVERTICAL.occlusionType: czy zagięcie lub zawias zasłania część wyświetlacza,NONElubFULL.isSeparating: czy zagięcie lub zawias tworzy 2 logiczne obszary wyświetlania, prawda lub fałsz.
Urządzenie składane, które jest HALF_OPENED, zawsze zgłasza isSeparating jako prawdę, ponieważ ekran jest podzielony na 2 obszary wyświetlania. Ponadto isSeparating jest zawsze prawdą na urządzeniu z 2 ekranami, gdy aplikacja obejmuje oba ekrany.
Właściwość FoldingFeature bounds (dziedziczona z DisplayFeature)
reprezentuje prostokąt ograniczający funkcji składania, takiej 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.
}
}
}
}
Tryb stołu
Korzystając z informacji zawartych w obiekcie FoldingFeature, aplikacja może obsługiwać pozycje takie jak tryb stołu, w którym telefon leży na powierzchni, zawias jest w pozycji poziomej, a ekran składany jest otwarty w połowie.
Tryb stołu umożliwia użytkownikom wygodne korzystanie z telefonów bez konieczności trzymania ich w dłoni. Tryb stołu świetnie sprawdza się podczas oglądania multimediów, robienia zdjęć i prowadzenia rozmów wideo.
Aby sprawdzić
czy urządzenie jest w trybie stołu, użyj FoldingFeature.State i FoldingFeature.Orientation:
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 wiesz, że urządzenie jest w trybie stołu, odpowiednio zaktualizuj układ aplikacji. W przypadku aplikacji multimedialnych oznacza to zwykle umieszczenie odtwarzania w części strony widocznej na ekranie oraz umieszczenie elementów sterujących i treści dodatkowych tuż za nim, aby zapewnić wygodne oglądanie lub słuchanie bez użycia rąk.
W Androidzie 15 (poziom interfejsu API 35) i nowszym możesz wywołać synchroniczny interfejs API, aby wykryć, czy urządzenie obsługuje tryb stołu, niezależnie od jego bieżącego stanu.
Interfejs API udostępnia listę pozycji obsługiwanych przez urządzenie. Jeśli lista zawiera tryb stołu, możesz podzielić układ aplikacji, aby obsługiwać tę pozycję, i przeprowadzić testy A/B interfejsu aplikacji w układach trybu stołu i pełnoekranowych.
Kotlin
if (WindowSdkExtensions.getInstance().extensionVersion >= 6) { val postures = WindowInfoTracker.getOrCreate(context).supportedPostures if (postures.contains(SupportedPosture.TABLETOP)) { // 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
MediaPlayerActivityAplikacja: dowiedz się, jak używać Media3 Exoplayer i WindowManager do tworzenia odtwarzacza wideo dostosowanego do składania.Optymalizacja aplikacji aparatu na urządzeniach składanych za pomocą Jetpack WindowManager codelab: dowiedz się, jak zaimplementować tryb stołu w aplikacjach do fotografowania. Wyświetl wizjer w górnej połowie ekranu (część strony widoczna na ekranie), a elementy sterujące w dolnej połowie (część strony widoczna po przewinięciu).
Tryb książki
Kolejną unikalną funkcją urządzeń składanych jest tryb książki, w którym urządzenie jest otwarte w połowie, a zawias jest w pozycji pionowej. Tryb książki świetnie sprawdza się podczas czytania e-booków. Dzięki układowi dwustronicowemu na dużym ekranie składanym, który jest otwarty jak oprawiona książka, tryb książki zapewnia wrażenia podobne do czytania prawdziwej książki.
Można go też używać do fotografowania, jeśli chcesz robić zdjęcia bez użycia rąk w innym formacie obrazu.
Tryb książki zaimplementuj za pomocą tych samych technik co tryb stołu. 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ę zmienić w wyniku zmiany konfiguracji urządzenia, np. gdy urządzenie jest złożone lub rozłożone, obrócone lub gdy rozmiar okna jest zmieniany w trybie wielu okien.
Klasa WindowMetricsCalculator w Jetpack WindowManager umożliwia pobieranie bieżących i maksymalnych danych okna. Podobnie jak platforma
WindowMetrics wprowadzona w poziomie interfejsu API 30, WindowManager
WindowMetrics udostępnia granice okna, ale interfejs API jest wstecznie zgodny
z poziomem interfejsu API 14.
Zobacz Używanie klas rozmiaru okna.
Dodatkowe materiały
Przykłady
- Jetpack WindowManager: przykład użycia biblioteki Jetpack WindowManager
- Jetcaster : implementacja trybu stołu za pomocą Compose
Codelabs
- Obsługa urządzeń składanych i z 2 ekranami za pomocą Jetpack WindowManager
- Optymalizacja aplikacji aparatu na urządzeniach składanych za pomocą Jetpack WindowManager