Zgodność urządzeń wejściowych na dużych ekranach

Na urządzeniach z dużym ekranem użytkownicy często korzystają z aplikacji za pomocą klawiatury, myszy, trackpada, rysika lub kontrolera. Aby umożliwić aplikacji przyjmowanie danych wejściowych z zewnętrznych urządzeń:

  • Sprawdź podstawowe funkcje obsługi klawiatury, takie jak Ctrl+Z, aby cofnąć, Ctrl+C, aby kopiować, i Ctrl+S, aby zapisać. Aby zobaczyć listę domyślnych skrótów klawiszowych, przeczytaj artykuł Obsługa działań na klawiaturze.
  • Testowanie zaawansowanego wsparcia klawiatury, na przykład TabEnter do nawigacji za pomocą klawiszy kursora, Enter do potwierdzenia wpisywania tekstu oraz Spacja do odtwarzania i wstrzymywania w aplikacjach multimedialnych.
  • Sprawdź podstawowe interakcje z myszą, w tym kliknięcie prawym przyciskiem myszy, aby otworzyć menu kontekstowe, zmiany ikony podczas najeżdżania kursorem oraz zdarzenia kółka myszy lub przewijania trackpada w komponentach niestandardowych.
  • Testuj urządzenia wejściowe związane z konkretną aplikacją, takie jak rysiki, kontrolery gier i kontrolery MIDI aplikacji muzycznych.
  • Rozważ zaimplementowanie zaawansowanego wsparcia dla danych wejściowych, które wyróżni Twoją aplikację na tle innych na komputerach. Może to być np. obsługa touchpada jako cross-fadera w aplikacjach do miksowania, przechwytywanie myszy w przypadku gier czy skróty klawiszowe dla użytkowników, którzy korzystają głównie z klawiatury.

Klawiatura

Sposób, w jaki aplikacja reaguje na dane wejściowe z klawiatury, wpływa na wrażenia użytkownika na dużym ekranie. Istnieją 3 rodzaje danych wprowadzanych za pomocą klawiatury: nawigacja, klawisze i skróty.

Nawigacja za pomocą klawiatury jest rzadko stosowana w aplikacji zorientowanej na dotyk, ale użytkownicy oczekują jej, gdy korzystają z aplikacji i mają dostęp do klawiatury. Nawigacja za pomocą klawiatury może być niezbędna na telefonach, tabletach, składanych urządzeniach i komputerach stacjonarnych dla użytkowników, którzy potrzebują ułatwień dostępu.

W przypadku wielu aplikacji nawigacja za pomocą klawiszy strzałek i klawisza Tab jest obsługiwana automatycznie przez platformę Androida. Na przykład niektóre komponenty są domyślnie możliwe do skupienia, np. Button lub komponent z modyfikatorem clickable. Nawigacja za pomocą klawiatury powinna działać bez dodatkowego kodu. Aby umożliwić nawigację za pomocą klawiatury w przypadku niestandardowych komponentów, które domyślnie nie są możliwe do zaznaczenia, dodaj modyfikator focusable:

var color by remember { mutableStateOf(Green) }
Box(
    Modifier
        .background(color)
        .onFocusChanged { color = if (it.isFocused) Blue else Green }
        .focusable()
) {
    Text("Focusable 1")
}

Więcej informacji znajdziesz w artykule Ustawianie komponentu w stanie fokusowania.

Gdy funkcja focus jest włączona, platforma Android tworzy mapowanie nawigacyjne wszystkich elementów, na których można się skupić, na podstawie ich pozycji. Wszystko działa zwykle zgodnie z oczekiwaniami i nie wymaga dalszego rozwoju.

Jednak Compose nie zawsze określa prawidłowe następne miejsce w przypadku nawigacji za pomocą kart w przypadku złożonych komponentów, takich jak karty czy listy, gdy jeden z tych komponentów jest elementem poziomym, który nie jest w pełni widoczny.

Aby kontrolować zachowanie fokusa, dodaj modyfikator focusGroup do nadrzędnego komponentu kolekcji komponentów. Wyróżnienie przenosi się na grupę, a potem przez grupę, zanim przejdzie do następnego elementu, na którym można ustawić wyróżnienie, na przykład:

Row {
    Column(Modifier.focusGroup()) {
        Button({}) { Text("Row1 Col1") }
        Button({}) { Text("Row2 Col1") }
        Button({}) { Text("Row3 Col1") }
    }
    Column(Modifier.focusGroup()) {
        Button({}) { Text("Row1 Col2") }
        Button({}) { Text("Row2 Col2") }
        Button({}) { Text("Row3 Col2") }
    }
}

Więcej informacji znajdziesz w artykule Zapewnienie spójnej nawigacji w grupach fokusowych.

Sprawdź dostęp do każdego elementu interfejsu aplikacji tylko za pomocą klawiatury. Elementy używane często powinny być dostępne bez myszy lub ekranu dotykowego.

Pamiętaj, że obsługa klawiatury może być niezbędna dla użytkowników z potrzebami w zakresie ułatwień dostępu.

Kombinacje klawiszy

W przypadku wprowadzania tekstu, które jest obsługiwane przez klawiaturę ekranową (IME), na przykład TextField aplikacje powinny działać zgodnie z oczekiwaniami na urządzeniach z dużym ekranem bez konieczności dodatkowej pracy programistów. W przypadku naciśnięć klawiszy, których nie można przewidzieć w ramach frameworku, aplikacje muszą samodzielnie obsługiwać takie działanie. Jest to szczególnie ważne w przypadku aplikacji z widokami niestandardowymi.

Przykładami są aplikacje do czatu, które używają klawisza Enter do wysyłania wiadomości, aplikacje multimedialne, które uruchamiają i zawieszają odtwarzanie za pomocą klawisza spacji, oraz gry, w których ruchem kieruje się za pomocą klawiszy w, a, sd.

Poszczególne naciśnięcia klawiszy możesz obsługiwać za pomocą modyfikatora onKeyEvent, który przyjmuje funkcję lambda wywoływaną, gdy zmodyfikowany komponent otrzyma kluczowe zdarzenie. Właściwość KeyEvent#type pozwala określić, czy zdarzenie to naciśnięcie klawisza (KeyDown) czy zwolnienie klawisza (KeyUp):

Box(
    modifier = Modifier.focusable().onKeyEvent {
        if(
            it.type == KeyEventType.KeyUp &&
            it.key == Key.S
        ) {
            doSomething()
            true
        } else {
            false
        }
    }
)  {
    Text("Press S key")
}

Możesz też zastąpić wywołanie zwrotne onKeyUp() i dodać oczekiwane działanie dla każdego otrzymanego klucza kodu:

override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
    return when (keyCode) {
        KeyEvent.KEYCODE_ENTER -> {
            sendChatMessage()
            true
        }
        KeyEvent.KEYCODE_SPACE -> {
            playOrPauseMedia()
            true
        }
        else -> super.onKeyUp(keyCode, event)
    }
}

Gdy klucz zostanie zwolniony, nastąpi zdarzenie onKeyUp. Dzięki wywołaniu zwrotnemu aplikacje nie muszą przetwarzać wielu zdarzeń onKeyDown, jeśli przycisk jest przytrzymywany lub zwalniany powoli. Gry i aplikacje, które muszą wykrywać moment naciśnięcia klawisza lub czy użytkownik przytrzymuje przycisk, mogą nasłuchiwać zdarzenia onKeyDown i same obsługiwać powtarzające się zdarzenia onKeyDown.

Więcej informacji znajdziesz w artykule Zarządzanie działaniami klawiatury.

Skróty

Podczas korzystania z klawiatury sprzętowej należy używać zwykłych skrótów klawiszowych, które obejmują klawisze Ctrl, Alt, ShiftMeta. Jeśli aplikacja nie obsługuje skrótów, może to być dla użytkowników frustrujące. Zaawansowani użytkownicy doceniają też skróty do często używanych zadań w poszczególnych aplikacjach. Skróty ułatwiają korzystanie z aplikacji i odróżniają ją od aplikacji, które nie mają skrótów.

Do najpopularniejszych skrótów należą Ctrl+S (zapisz), Ctrl+Z (cofnij) i Ctrl+Shift+Z (ponów). Listę domyślnych skrótów znajdziesz w artykule Obsługa działań na klawiaturze.

Obiekt KeyEvent ma te atrybuty, które wskazują, czy wciśnięto klawisze modyfikujące:

Przykład:

Box(
    Modifier.onKeyEvent {
        if (it.isAltPressed && it.key == Key.A) {
            println("Alt + A is pressed")
            true
        } else {
            false
        }
    }
    .focusable()
)

Więcej informacji znajdziesz w tych artykułach:

Rysik

Wiele urządzeń z dużym ekranem jest wyposażonych w rysik. Aplikacje na Androida obsługują rysiki jako dane z ekranu dotykowego. Niektóre urządzenia mogą mieć też tablet do rysowania z podłączeniem USB lub Bluetooth, np. Wacom Intuos. Aplikacje na Androida mogą odbierać dane z Bluetooth, ale nie z USB.

Aby uzyskać dostęp do obiektów rysika MotionEvent, dodaj modyfikator pointerInteropFilterdo powierzchni do rysowania. Zaimplementuj klasę ViewModel z metodą, która przetwarza zdarzenia związane z ruchu. Przekaż tę metodę jako lambda onTouchEvent modyfikatora pointerInteropFilter:

@Composable
@OptIn(ExperimentalComposeUiApi::class)
fun DrawArea(modifier: Modifier = Modifier) {
   Canvas(modifier = modifier
       .clipToBounds()
       .pointerInteropFilter {
           viewModel.processMotionEvent(it)
       }

   ) {
       // Drawing code here.
   }
}

Obiekt MotionEvent zawiera informacje o zdarzeniu:

Punkty historyczne

Android grupowanie zdarzeń wejściowych i przesyła je raz na ramkę. Stylus może zgłaszać zdarzenia z wielokrotnie większą częstotliwością niż wyświetlacz. Podczas tworzenia aplikacji do rysowania sprawdź, czy nie doszło do żadnych zdarzeń w ostatnim czasie, korzystając z interfejsów API:getHistorical

Odrzucenie palmy

Gdy użytkownicy rysują, piszą lub wchodzą w interakcję z aplikacją za pomocą rysika, czasami dotykają ekranu dłonią. Zdarzenie dotyku (ustawione na ACTION_DOWN lub ACTION_POINTER_DOWN) może być zgłaszane do aplikacji, zanim system wykryje i ignoruje przypadkowe dotknięcie dłonią.

Android anuluje zdarzenia dotyku dłoni, wysyłając MotionEvent. Jeśli aplikacja otrzyma sygnał ACTION_CANCEL, anuluj gest. Jeśli Twoja aplikacja otrzymuje odpowiedź ACTION_POINTER_UP, sprawdź, czy ustawiona jest wartość FLAG_CANCELED. Jeśli tak, anuluj gest.

Nie sprawdzaj tylko FLAG_CANCELED. W Androidzie 13 (poziom API 33) lub nowszym system ustawia flagę FLAG_CANCELED dla zdarzeń ACTION_CANCEL, ale nie ustawia jej w starszych wersjach Androida.

Android 12

W przypadku Androida 12 (poziom interfejsu API 32) lub starszego wykrywanie odrzucenia dłoni jest możliwe tylko w przypadku zdarzeń dotykowych z pojedynczym wskaźnikiem. Jeśli dotykowe polecenie palcem jest jedynym wskaźnikiem, system anuluje zdarzenie, ustawiając wartość ACTION_CANCEL w obiekcie zdarzenia ruchu. Jeśli inne wskaźniki są w pozycji w dół, system ustawia ACTION_POINTER_UP, co jest niewystarczające do wykrywania odrzucenia dłoni.

Android 13

W Androidzie 13 (poziom API 33) lub nowszym, jeśli dotyk dłoni jest jedynym wskaźnikiem, system anuluje zdarzenie, ustawiając wartości ACTION_CANCELFLAG_CANCELED w obiekcie zdarzenia ruchu. Jeśli inne wskaźniki są w dół, system ustawia ACTION_POINTER_UPFLAG_CANCELED.

Gdy aplikacja otrzyma zdarzenie ruchu z wartością ACTION_POINTER_UP, sprawdź, czy występuje zdarzenie FLAG_CANCELED, aby określić, czy oznacza ono odrzucenie dłoni (lub anulowanie innego zdarzenia).

Aplikacje do notatek

ChromeOS ma specjalny zamiar, który wyświetla zarejestrowane aplikacje do robienia notatek. Aby zarejestrować aplikację jako aplikację do tworzenia notatek, dodaj do pliku manifestu aplikacji te informacje:

<intent-filter>
    <action android:name="org.chromium.arc.intent.action.CREATE_NOTE" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

Gdy aplikacja zostanie zarejestrowana w systemie, użytkownik może ją wybrać jako domyślną aplikację do tworzenia notatek. Gdy użytkownik poprosi o utworzenie nowej notatki, aplikacja powinna utworzyć pustą notatkę gotową do wprowadzania danych za pomocą rysika. Gdy użytkownik chce dodać adnotację do obrazu (np. zrzutu ekranu lub pobranego obrazu), aplikacja uruchamia się z ClipData, zawierającym co najmniej 1 element z identyfikatorem URI content://. Aplikacja powinna utworzyć notatkę, która używa pierwszego załączonego obrazu jako obrazu tła, i wejść w tryb, w którym użytkownik może rysować na ekranie za pomocą rysika.

Testowanie intencji dotyczących robienia notatek bez rysika

[TBD remove section.]

Aby sprawdzić, czy aplikacja reaguje prawidłowo na intencje dotyczące tworzenia notatek bez aktywnego rysika, wyświetl opcje tworzenia notatek w ChromeOS w ten sposób:

  1. Przejdź do trybu programisty i zezwól na zapis na urządzeniu
  2. Aby otworzyć terminal, naciśnij Ctrl+Alt+F2.
  3. Uruchom polecenie sudo vi /etc/chrome_dev.conf.
  4. Naciśnij i, aby edytować i dodać --ash-enable-palette do nowego wiersza na końcu pliku.
  5. Zapisz, naciskając Esc, a następnie wpisując :, w, q i naciskając Enter
  6. Aby wrócić do zwykłego interfejsu ChromeOS, naciśnij Ctrl+Alt+F1.
  7. Wyloguj się, a potem zaloguj ponownie.

Na półce powinno teraz pojawić się menu rysika:

  • Kliknij przycisk rysika na półce i wybierz Nowa notatka. Powinien otworzyć się pusty notatnik do rysowania.
  • Zrób zrzut ekranu. Na półce wybierz przycisk rysika > Zrób zrzut ekranu lub pobierz obraz. W powiadomieniu powinna być opcja Adnotacja obrazu. Powinien się uruchomić obraz w aplikacji z możliwością dodania adnotacji.

Obsługa myszy i touchpada

Większość aplikacji musi obsługiwać tylko 3 duże zdarzenia związane z ekranem: kliknięcie prawym przyciskiem myszy, najechanie kursoremprzeciągnięcie i upuszczenie.

Kliknięcie prawym przyciskiem

Wszystkie działania, które powodują wyświetlenie menu kontekstowego w aplikacji, np. dotknięcie i przytrzymanie elementu na liście, powinny również reagować na zdarzenia związane z kliknięciem prawym przyciskiem myszy.

Aby obsługiwać zdarzenia kliknięcia prawym przyciskiem myszy, aplikacje powinny zarejestrować: View.OnContextClickListener

Box(modifier = Modifier.fillMaxSize()) {
    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { context ->
            val rootView = FrameLayout(context)
            val onContextClickListener =
                View.OnContextClickListener { view ->
                    showContextMenu()
                    true
                }
            rootView.setOnContextClickListener(onContextClickListener)
            rootView
        },
    )
}

Szczegółowe informacje o tworzeniu menu kontekstowych znajdziesz w artykule Tworzenie menu kontekstowych.

Najechanie

Możesz sprawić, że układy aplikacji będą wyglądać bardziej profesjonalnie i łatwiej będzie z nich korzystać, obsługując zdarzenia związane z najechaniem kursorem. Dotyczy to szczególnie niestandardowych komponentów:

Oto 2 najczęstsze przykłady:

  • Wskazanie użytkownikom, czy element jest interaktywny, np. czy można go kliknąć lub edytować, przez zmianę ikony kursora myszy
  • Dodawanie informacji wizualnych do elementów na dużej liście lub siatce po najechaniu na nie kursorem.

Przeciągnij i upuść

W środowisku wielookiennego użytkownicy oczekują, że będą mogli przeciągać i upuszczać elementy między aplikacjami. Dotyczy to komputerów, a także tabletów, telefonów i urządzeń składanych w trybie podzielonego ekranu.

Zastanów się, czy użytkownicy będą przeciągać elementy do aplikacji. Na przykład edytory zdjęć powinny oczekiwać zdjęć, odtwarzacze dźwięku powinny oczekiwać plików audio, a programy do rysowania powinny oczekiwać zdjęć.

Aby dodać obsługę przeciągania i upuszczania, zapoznaj się z artykułem Przeciąganie i upuszczanie oraz przeczytaj wpis na blogu Android na ChromeOS – implementacja przeciągania i upuszczania.

Ważne informacje dotyczące ChromeOS

Zaawansowana obsługa wskaźnika

Aplikacje, które obsługują zaawansowane dane wejściowe myszy i touchpada, powinny zaimplementować modyfikator pointerInput, aby uzyskać PointerEvent:

@Composable
private fun LogPointerEvents(filter: PointerEventType? = null) {
    var log by remember { mutableStateOf("") }
    Column {
        Text(log)
        Box(
            Modifier
                .size(100.dp)
                .background(Color.Red)
                .pointerInput(filter) {
                    awaitPointerEventScope {
                        while (true) {
                            val event = awaitPointerEvent()
                            // handle pointer event
                            if (filter == null || event.type == filter) {
                                log = "${event.type}, ${event.changes.first().position}"
                            }
                        }
                    }
                }
        )
    }
}

Sprawdź obiekt PointerEvent, aby określić:

Kontrolery do gier

Niektóre urządzenia z Androidem o dużym ekranie obsługują do 4 kontrolerów gier. Do obsługi kontrolerów gier używaj standardowych interfejsów API kontrolerów gier na Androida (patrz Obsługa kontrolerów gier).

Przyciski kontrolera są mapowane na wspólne wartości zgodnie ze wspólnym mapowaniem. Nie wszyscy producenci kontrolerów do gier stosują jednak te same konwencje mapowania. Możesz zapewnić użytkownikom znacznie lepsze wrażenia, jeśli zezwolisz im na wybór różnych popularnych mapowań kontrolerów. Więcej informacji znajdziesz w artykule Przetwarzanie naciśnięć przycisków na padzie do gier.

Tryb translacji podczas wprowadzania

ChromeOS domyślnie włącza tryb translacji danych wejściowych. W przypadku większości aplikacji na Androida ten tryb pomaga aplikacjom działać zgodnie z oczekiwaniami w środowisku komputera. Przykłady obejmują automatyczne włączanie przewijania dwoma palcami na touchpadzie, przewijanie kółkiem myszy oraz mapowanie surowych współrzędnych wyświetlacza na współrzędne okna. Zazwyczaj deweloperzy aplikacji nie muszą sami wdrażać tych zachowań.

Jeśli aplikacja implementuje niestandardowe działanie wprowadzania danych, np. definiuje niestandardowe działanie ściśnięcia 2 palcami na touchpadzie, lub jeśli te tłumaczenia wprowadzania danych nie zapewniają zdarzeń wprowadzania danych oczekiwanych przez aplikację, możesz wyłączyć tryb tłumaczenia wprowadzania danych, dodając do pliku AndroidManifest.xml ten tag:

<uses-feature
    android:name="android.hardware.type.pc"
    android:required="false" />

Dodatkowe materiały