Składane urządzenia zapewniają wyjątkowe wrażenia podczas oglądania. Tryb wyświetlacza tylnego i tryb podwójnego ekranu umożliwiają tworzenie specjalnych funkcji wyświetlacza dla składanych urządzeń, takich jak podgląd selfie z tylnego aparatu czy jednoczesne wyświetlanie różnych treści na ekranie wewnętrznym i zewnętrznym.
Tryb wyświetlania tylnego wyświetlacza
Zwykle po rozłożeniu składanego urządzenia aktywny jest tylko ekran wewnętrzny. Tryb zewnętrznego wyświetlacza pozwala przenieść aktywność na zewnętrzny ekran składanego urządzenia, który zwykle jest odwrócony od użytkownika, gdy urządzenie jest rozłożone. Wewnętrzny ekran wyłączy się automatycznie.
Nowatorskim rozwiązaniem jest wyświetlanie podglądu aparatu na ekranie zewnętrznym, dzięki czemu użytkownicy mogą robić selfie tylnym aparatem, który zwykle jest znacznie lepszy od przedniego.
Aby aktywować tryb wyświetlacza tylnego, użytkownicy muszą odpowiedzieć na okno dialogowe, aby umożliwić aplikacji przełączanie ekranów, na przykład:
Okno tworzy system, więc nie musisz nic programować. W zależności od stanu urządzenia wyświetlane są różne okna, np. system kieruje użytkowników, aby otwierali urządzenie, gdy urządzenie jest zamknięte. Nie możesz dostosować tego okna dialogowego, a może się ono różnić na urządzeniach różnych producentów OEM.
Tryb tylnego wyświetlacza możesz wypróbować w aplikacji aparatu Pixel Fold. Zapoznaj się z przykładową implementacją w ramach ćwiczenia w Codelabs pt. Optymalizowanie aplikacji aparatu na urządzeniach składanych przy użyciu Jetpack WindowManager.
Tryb dwóch ekranów
Tryb 2 ekranów umożliwia jednoczesne wyświetlanie treści na obu wyświetlaczach urządzenia składanego. Tryb dwóch ekranów jest dostępny na urządzeniu Pixel Fold z Androidem 14 (poziom interfejsu API 34) lub nowszym.
Przykładem może być korzystanie z tłumaczenia na dwa ekrany.
Automatyczne włączanie trybów
Za pomocą interfejsów API Jetpack WindowManager możesz uzyskać dostęp do trybu tylnego wyświetlacza i trybu podwójnego ekranu, począwszy od biblioteki w wersji 1.2.0-beta03.
Dodaj zależność WindowManager do pliku build.gradle
modułu aplikacji:
Odlotowe
dependencies { implementation "androidx.window:window:1.2.0-beta03" }
Kotlin
dependencies { implementation("androidx.window:window:1.2.0-beta03") }
Punkt wejścia to WindowAreaController
, który podaje informacje i zachowania związane z przenoszeniem okien między wyświetlaczami lub między obszarami wyświetlania na urządzeniu. WindowAreaController
umożliwia wysyłanie zapytań do listy dostępnych obiektów WindowAreaInfo
.
Aby otworzyć interfejs WindowAreaSession
, który reprezentuje funkcję aktywnego obszaru okna, użyj przycisku WindowAreaInfo
. Aby określić dostępność konkretnego WindowAreaCapability
, użyj elementu WindowAreaSession
.
Każda z nich jest powiązana z określonym WindowAreaCapability.Operation
.
W wersjach 1.2.0-beta03 Jetpack WindowManager obsługuje dwa rodzaje operacji:
WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREA
, służący do uruchamiania trybu Dual Screen;WindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA
, który służy do uruchamiania trybu wyświetlacza tylnego
Oto przykład deklarowania zmiennych dla trybu wyświetlacza tylnego i trybu podwójnego ekranu w głównym działaniu aplikacji:
Kotlin
private lateinit var windowAreaController: WindowAreaController private lateinit var displayExecutor: Executor private var windowAreaSession: WindowAreaSession? = null private var windowAreaInfo: WindowAreaInfo? = null private var capabilityStatus: WindowAreaCapability.Status = WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED private val dualScreenOperation = WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREA private val rearDisplayOperation = WindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA
Java
private WindowAreaControllerCallbackAdapter windowAreaController = null; private Executor displayExecutor = null; private WindowAreaSessionPresenter windowAreaSession = null; private WindowAreaInfo windowAreaInfo = null; private WindowAreaCapability.Status capabilityStatus = WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED; private WindowAreaCapability.Operation dualScreenOperation = WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREA; private WindowAreaCapability.Operation rearDisplayOperation = WindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA;
Aby zainicjować zmienne w metodzie onCreate()
w aplikacji:
Kotlin
displayExecutor = ContextCompat.getMainExecutor(this) windowAreaController = WindowAreaController.getOrCreate() lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { windowAreaController.windowAreaInfos .map { info -> info.firstOrNull { it.type == WindowAreaInfo.Type.TYPE_REAR_FACING } } .onEach { info -> windowAreaInfo = info } .map { it?.getCapability(operation)?.status ?: WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED } .distinctUntilChanged() .collect { capabilityStatus = it } } }
Java
displayExecutor = ContextCompat.getMainExecutor(this); windowAreaController = new WindowAreaControllerCallbackAdapter(WindowAreaController.getOrCreate()); windowAreaController.addWindowAreaInfoListListener(displayExecutor, this); windowAreaController.addWindowAreaInfoListListener(displayExecutor, windowAreaInfos -> { for(WindowAreaInfo newInfo : windowAreaInfos){ if(newInfo.getType().equals(WindowAreaInfo.Type.TYPE_REAR_FACING)){ windowAreaInfo = newInfo; capabilityStatus = newInfo.getCapability(presentOperation).getStatus(); break; } } });
Zanim rozpoczniesz działanie, sprawdź dostępność danej funkcji:
Kotlin
when (capabilityStatus) { WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED -> { // The selected display mode is not supported on this device. } WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNAVAILABLE -> { // The selected display mode is not available. } WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE -> { // The selected display mode is available and can be enabled. } WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE -> { // The selected display mode is already active. } else -> { // The selected display mode status is unknown. } }
Java
if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED)) { // The selected display mode is not supported on this device. } else if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNAVAILABLE)) { // The selected display mode is not available. } else if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE)) { // The selected display mode is available and can be enabled. } else if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE)) { // The selected display mode is already active. } else { // The selected display mode status is unknown. }
Tryb dwóch ekranów
Ten przykład zamyka sesję, jeśli funkcja jest już aktywna, lub w inny sposób wywołuje funkcję presentContentOnWindowArea()
:
Kotlin
fun toggleDualScreenMode() { if (windowAreaSession != null) { windowAreaSession?.close() } else { windowAreaInfo?.token?.let { token -> windowAreaController.presentContentOnWindowArea( token = token, activity = this, executor = displayExecutor, windowAreaPresentationSessionCallback = this ) } } }
Java
private void toggleDualScreenMode() { if(windowAreaSession != null) { windowAreaSession.close(); } else { Binder token = windowAreaInfo.getToken(); windowAreaController.presentContentOnWindowArea( token, this, displayExecutor, this); } }
Zwróć uwagę na użycie głównej aktywności w aplikacji jako argumentu WindowAreaPresentationSessionCallback
.
Interfejs API korzysta z metody listenera: gdy wysyłasz żądanie wyświetlenia treści na drugim wyświetlaczu składanego urządzenia, inicjujesz sesję, która jest zwracana za pomocą metody onSessionStarted()
listenera. Po zamknięciu sesji otrzymasz potwierdzenie w ramach metody onSessionEnded()
.
Aby utworzyć listenera, zaimplementuj interfejs WindowAreaPresentationSessionCallback
:
Kotlin
class MainActivity : AppCompatActivity(), windowAreaPresentationSessionCallback
Java
public class MainActivity extends AppCompatActivity implements WindowAreaPresentationSessionCallback
Listener musi zaimplementować metody onSessionStarted()
, onSessionEnded(),
i onContainerVisibilityChanged()
. Metody wywołania zwrotnego informują o stanie sesji i umożliwiają odpowiednie zaktualizowanie aplikacji.
Wywołanie zwrotne onSessionStarted()
otrzymuje argument WindowAreaSessionPresenter
. Argument to kontener, który umożliwia dostęp do obszaru okna i wyświetlanie treści. System może automatycznie zamknąć prezentację, gdy użytkownik opuści główne okno aplikacji, lub zamknąć prezentację, wywołując metodę WindowAreaSessionPresenter#close()
.
W przypadku innych wywołań zwrotnych na potrzeby uproszczenia sprawdź w ciele funkcji, czy nie ma błędów, i zapisz stan:
Kotlin
override fun onSessionStarted(session: WindowAreaSessionPresenter) { windowAreaSession = session val view = TextView(session.context) view.text = "Hello world!" session.setContentView(view) } override fun onSessionEnded(t: Throwable?) { if(t != null) { Log.e(logTag, "Something was broken: ${t.message}") } } override fun onContainerVisibilityChanged(isVisible: Boolean) { Log.d(logTag, "onContainerVisibilityChanged. isVisible = $isVisible") }
Java
@Override public void onSessionStarted(@NonNull WindowAreaSessionPresenter session) { windowAreaSession = session; TextView view = new TextView(session.getContext()); view.setText("Hello world, from the other screen!"); session.setContentView(view); } @Override public void onSessionEnded(@Nullable Throwable t) { if(t != null) { Log.e(logTag, "Something was broken: ${t.message}"); } } @Override public void onContainerVisibilityChanged(boolean isVisible) { Log.d(logTag, "onContainerVisibilityChanged. isVisible = " + isVisible); }
Aby zachować spójność w ekosystemie, użyj oficjalnego ikony Dual Screen, aby poinformować użytkowników, jak włączyć lub wyłączyć tryb Dual Screen.
Przykładowy działający kod znajdziesz w pliku DualScreenActivity.kt.
Tryb tylnego wyświetlacza
Podobnie jak w przypadku trybu 2 ekranów poniższy przykład funkcji toggleRearDisplayMode()
zamyka sesję, jeśli jest już aktywna lub wywołuje funkcję transferActivityToWindowArea()
:
Kotlin
fun toggleRearDisplayMode() { if(capabilityStatus == WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE) { if(windowAreaSession == null) { windowAreaSession = windowAreaInfo?.getActiveSession( operation ) } windowAreaSession?.close() } else { windowAreaInfo?.token?.let { token -> windowAreaController.transferActivityToWindowArea( token = token, activity = this, executor = displayExecutor, windowAreaSessionCallback = this ) } } }
Java
void toggleDualScreenMode() { if(capabilityStatus == WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE) { if(windowAreaSession == null) { windowAreaSession = windowAreaInfo.getActiveSession( operation ) } windowAreaSession.close() } else { Binder token = windowAreaInfo.getToken(); windowAreaController.transferActivityToWindowArea(token, this, displayExecutor, this); } }
W tym przypadku wyświetlana aktywność jest wykorzystywana jako WindowAreaSessionCallback
, co jest prostsze do wdrożenia, ponieważ wywołanie zwrotne nie odbiera prezentacji, która umożliwia wyświetlenie treści w obszarze okna, ale przenosi całą aktywność do innego obszaru:
Kotlin
override fun onSessionStarted() { Log.d(logTag, "onSessionStarted") } override fun onSessionEnded(t: Throwable?) { if(t != null) { Log.e(logTag, "Something was broken: ${t.message}") } }
Java
@Override public void onSessionStarted(){ Log.d(logTag, "onSessionStarted"); } @Override public void onSessionEnded(@Nullable Throwable t) { if(t != null) { Log.e(logTag, "Something was broken: ${t.message}"); } }
Aby zachować spójność w ekosystemie, użyj oficjalnego ikony tylnego aparatu, aby poinformować użytkowników, jak włączyć lub wyłączyć tryb wyświetlacza tylnego.
Dodatkowe materiały
- Zoptymalizuj aplikację aparatu na urządzeniach składanych dzięki Jetpack WindowManager ćwiczenia z programowania
androidx.window.area
podsumowanie pakietu- Przykładowy kod Jetpack WindowManager: