Obsługa okna pulpitu

Tryb okien na pulpicie umożliwia użytkownikom uruchamianie wielu aplikacji jednocześnie w oknach aplikacji o zmienianym rozmiarze, co zapewnia wszechstronność i wygodę podobną do korzystania z komputera.

Na ilustracji 1 widać organizację ekranu z włączonym trybem okien na pulpicie. Warto wiedzieć:

  • Użytkownicy mogą uruchamiać kilka aplikacji obok siebie jednocześnie.
  • Pasek aplikacji jest umieszczony na stałe u dołu wyświetlacza i pokazuje uruchomione aplikacje. Użytkownicy mogą przypinać aplikacje, aby mieć do nich szybki dostęp.
  • Nowy, konfigurowalny pasek nagłówka zdobi górę każdego okna i zawiera elementy sterujące, takie jak minimalizowanie i maksymalizowanie.
Rysunek 1. Tryb okien na pulpicie na tablecie.

Domyślnie aplikacje na tabletach z Androidem otwierają się na pełnym ekranie. Aby uruchomić aplikację w trybie okien na pulpicie, naciśnij i przytrzymaj uchwyt okna u góry ekranu, a następnie przeciągnij go w interfejsie, jak pokazano na rysunku 2.

Gdy aplikacja jest otwarta w trybie okien na pulpicie, inne aplikacje również otwierają się w oknach na pulpicie.

Rysunek 2. Naciśnij i przytrzymaj uchwyt okna aplikacji, a następnie przeciągnij go, aby włączyć tryb okien na pulpicie.

Użytkownicy mogą też wywołać tryb okien na pulpicie z menu, które pojawia się pod uchwytem okna po kliknięciu lub dotknięciu uchwytu, albo za pomocą skrótu klawiszowego klawisz Meta (Windows, Command lub Wyszukaj) + Ctrl + strzałka w dół.

Użytkownicy mogą wyjść z trybu okien na pulpicie, zamykając wszystkie aktywne okna lub chwytając uchwyt okna u góry okna i przeciągając aplikację na górę ekranu. Skrót klawiszowy Meta + H również zamyka okna na pulpicie i ponownie uruchamia aplikacje w trybie pełnoekranowym.

Aby wrócić do trybu okien na pulpicie, na ekranie Ostatnie kliknij lub wybierz kafelek przestrzeni na komputerze.

Zmiana rozmiaru i tryb zgodności

W trybie okien na pulpicie aplikacje z zablokowaną orientacją można dowolnie zmieniać. Oznacza to, że nawet jeśli aktywność jest zablokowana w orientacji pionowej, użytkownicy mogą nadal zmieniać rozmiar aplikacji na okno w orientacji poziomej.

Rysunek 3. Zmiana rozmiaru okna aplikacji, która może być używana tylko w orientacji pionowej, na orientację poziomą.

Aplikacje zadeklarowane jako niemożliwe do zmiany rozmiaru (czyli resizeableActivity = false) mają interfejs skalowany przy zachowaniu tego samego współczynnika proporcji.

Rysunek 4. Interfejs aplikacji bez możliwości zmiany rozmiaru jest skalowany wraz ze zmianą rozmiaru okna.

Aplikacje aparatu, które blokują orientację lub są zadeklarowane jako niemożliwe do zmiany rozmiaru, mają specjalne traktowanie wizjerów: okno można w pełni zmieniać, ale wizjer zachowuje ten sam format obrazu. Zakładając, że aplikacje zawsze działają w orientacji pionowej lub poziomej, programiści na stałe kodują lub w inny sposób przyjmują założenia, które prowadzą do błędnych obliczeń orientacji lub proporcji podglądu lub przechwyconego obrazu, co skutkuje rozciągniętymi, odwróconymi lub obróconymi obrazami.

Dopóki aplikacje nie będą w pełni obsługiwać responsywnych wizjerów aparatu, specjalne traktowanie zapewni bardziej podstawową obsługę, która ograniczy skutki błędnych założeń.

Więcej informacji o trybie zgodności w przypadku aplikacji aparatu znajdziesz w artykule Tryb zgodności urządzenia.

Rysunek 5. Wizjer kamery zachowuje format obrazu podczas zmiany rozmiaru okna.

Dostosowywane wcięcia nagłówka

Wszystkie aplikacje działające w trybie okien na pulpicie mają pasek nagłówka, nawet w trybie pełnoekranowym. Możesz dostosować ten pasek, aby zapobiec zasłanianiu zawartości aplikacji i wyświetlać niestandardowe elementy interfejsu bezpośrednio w przestrzeni nagłówka.

Chrome przed wdrożeniem niestandardowych nagłówków i po nim.
Rysunek 6. Chrome przed wdrożeniem niestandardowych nagłówków i po nim.

Implementacja

Aby narysować niestandardową treść na pasku nagłówka, najpierw ustaw tło paska nagłówka jako przezroczyste. Możesz to zrobić za pomocą flagi APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND z parametrem WindowInsetsController.

window.insetsController?.setSystemBarsAppearance(
    WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
    WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND
)

Gdy pasek nagłówka będzie przezroczysty, możesz dostosować obszar nagłówka do wyglądu aplikacji. Użyj WindowInsets.isCaptionBarVisible, aby wykryć, czy pasek jest obecny, i zastosować odpowiednią wysokość lub dopełnienie w układzie.

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun CaptionBar() {
    if (WindowInsets.isCaptionBarVisible) {
        Row(
            modifier = Modifier
                .windowInsetsTopHeight(WindowInsets.captionBar)
                .fillMaxWidth()
                .background(if (isSystemInDarkTheme()) Color.White else Color.Black),
            horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                text = "Caption Bar Title",
                style = MaterialTheme.typography.titleMedium,
                modifier = Modifier.padding(4.dp)
            )
        }
    }
}

  • setSystemBarsAppearance(appearance,mask): konfiguruje styl wizualny pasków systemowych. Pierwszy parametr określa flagi docelowego wyglądu, a drugi działa jako maska, która kontroluje, które konkretne flagi są modyfikowane.

  • windowInsetsTopHeight(): automatycznie ustawia wysokość elementu kompozycyjnego tak, aby pasowała do paska nagłówka systemu. Dzięki temu niestandardowe tło wypełnia obszar podpisu bez kodowania na stałe wartości pikseli.

  • WindowInsets.captionBar: podaje wymiary elementów sterujących okien na pulpicie (Zamknij, Maksymalizuj itp.), dzięki czemu interfejs użytkownika może automatycznie skalować się lub ukrywać po włączeniu lub wyłączeniu trybu okien na pulpicie.

Więcej informacji znajdziesz w artykule Informacje o wcięciach w oknie. Oprócz tytułu na pasku podpisu możesz wyświetlać inne elementy interfejsu, takie jak karty (jak w Google Chrome), paski wyszukiwania czy awatary profilu.

Interfejs użytkownika

Aby uniknąć nakładania się interfejsu na przyciski systemowe, Android 15 udostępnia metodę WindowInsets#getBoundingRects(). Metoda zwraca listę obiektów Rect reprezentujących obszary zajmowane przez elementy systemu. Pozostała przestrzeń na pasku podpisu to strefa bezpieczna, w której możesz bezpiecznie umieszczać niestandardowe treści.

.

Przełączaj wygląd elementów napisów systemowych w przypadku motywu jasnego i ciemnego za pomocą elementu APPEARANCE_LIGHT_CAPTION_BARS. Dostęp do wstawek uzyskasz, wpisując WindowInsets.Companion.captionBar() w trybie tworzenia lub WindowInsets.Type.captionBar() w widokach.

Więcej informacji znajdziesz w artykule Informacje o wcięciach w oknie.

Wielozadaniowość i obsługa wielu instancji

Wielozadaniowość jest podstawą trybu okien na pulpicie, a umożliwienie korzystania z wielu instancji aplikacji może znacznie zwiększyć produktywność użytkowników.

Od Androida 15 możesz używać PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI. Ustawiając tę właściwość w AndroidManifest.xml, określasz, że interfejs systemu powinien udostępniać opcje (np. przycisk „Nowe okno”) umożliwiające uruchamianie aplikacji w wielu instancjach.

<application>
    <property
        android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
        android:value="true" />
</application>

Uwaga: w trybie wielu okien i innych środowiskach z wieloma oknami, np. w trybie okien na pulpicie, nowe zadania otwierają się w nowym oknie. Dlatego za każdym razem, gdy aplikacja uruchamia wiele zadań, dokładnie sprawdzaj ścieżkę użytkownika.

Zarządzanie instancjami aplikacji za pomocą gestów przeciągania

W trybie wielu okien użytkownicy mogą uruchamiać nową instancję aplikacji, przeciągając element interfejsu (np. kartę lub dokument) z okna aplikacji. Użytkownicy mogą też przenosić elementy między różnymi instancjami tej samej aplikacji.

Rysunek 7. Uruchom nową instancję Chrome, przeciągając kartę z okna na komputerze.

Przenoszenie danych metodą „przeciągnij i upuść”

Aby skonfigurować komponent jako źródło przeciągania w przypadku przeciągania i upuszczania w trybie podzielonym, co umożliwia użytkownikom przeciąganie treści do innej instancji aplikacji lub tworzenie nowej instancji przez upuszczanie treści w pustym obszarze ekranu, użyj modyfikatora dragAndDropSource. W funkcji lambda zwróć DragAndDropTransferData, przekazując ClipData zawierający dane do przeniesienia oraz flagi konfigurujące działanie w trybie podzielonym.

Android 15 wprowadza 2 kluczowe flagi dotyczące trybu okien na pulpicie i interakcji z wieloma instancjami:

  • DRAG_FLAG_GLOBAL_SAME_APPLICATION: wskazuje, że operacja przeciągania może przekraczać granice okien (w przypadku wielu instancji tej samej aplikacji). Gdy funkcja startDragAndDrop() jest wywoływana z ustawioną tą flagą, tylko widoczne okna należące do tej samej aplikacji mogą uczestniczyć w operacji przeciągania i otrzymywać przeciąganą zawartość.

Modifier.dragAndDropSource { _ ->
    DragAndDropTransferData(
        clipData = ClipData.newPlainText("label", "Your data"),
        flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION
    )
}

Modifier.dragAndDropSource { _ ->
    val intent = Intent.makeMainActivity(activity.componentName).apply {
        putExtra("EXTRA_ITEM_ID", itemId)
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or
                Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
                Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
    }

    val pendingIntent = PendingIntent.getActivity(
        activity, 0, intent, PendingIntent.FLAG_IMMUTABLE
    )

    val data = ClipData(
        "Item $itemId",
        arrayOf(ClipDescription.MIMETYPE_TEXT_INTENT),
        ClipData.Item.Builder().setIntentSender(pendingIntent.intentSender).build()
    )

    DragAndDropTransferData(
        clipData = data,
        flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION or
                View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
    )
}

Odbieranie przeniesionych danych

Aby zaakceptować dane z innej instancji, użyj modyfikatora dragAndDropTarget. Jeśli dane pochodzą z innej instancji lub aplikacji, musisz wyraźnie poprosić o uprawnienia.

Modifier.dragAndDropTarget(
    shouldStartDragAndDrop = { event ->
        event.toAndroidDragEvent().clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)
    },
    target = object : DragAndDropTarget {
        override fun onDrop(event: DragAndDropEvent): Boolean {
            requestDragAndDropPermissions(activity, event.toAndroidDragEvent())
            val clipData = event.toAndroidDragEvent().clipData
            val item = clipData?.getItemAt(0)?.text
            if (item != null) {
                // Process the dropped text item here
            }
            return item != null
        }
    }
)

Najważniejsze kroki:

  • Filtr: użyj shouldStartDragAndDrop, aby sprawdzić, czy dane przychodzące (typ MIME) są obsługiwane.
  • Uprawnienia: wywołaj requestDragAndDropPermissions(event), aby uzyskać dostęp do danych.
  • Obsługa: wyodrębnij dane w wywołaniu zwrotnym onDrop.

Dodatkowe optymalizacje

Dostosuj uruchamianie aplikacji i przełączaj je z trybu okien na pulpicie na pełny ekran.

Określanie domyślnego rozmiaru i położenia

Nie wszystkie aplikacje, nawet te, których rozmiar można zmieniać, potrzebują dużego okna, aby oferować użytkownikom wartość. Możesz użyć metody ActivityOptions#setLaunchBounds(), aby określić domyślny rozmiar i pozycję podczas uruchamiania aktywności.

Włączanie trybu pełnoekranowego z przestrzeni na pulpicie

Aplikacje mogą przejść w tryb pełnoekranowy, wywołując funkcję Activity#requestFullScreenMode(). Ta metoda wyświetla aplikację na pełnym ekranie bezpośrednio z trybu okien na pulpicie.